<?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"
	>

<channel>
	<title>Katz Got Your Tongue?</title>
	<atom:link href="http://yehudakatz.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://yehudakatz.com</link>
	<description>Random Geek-Related Thoughts</description>
	<pubDate>Tue, 13 May 2008 01:54:00 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<item>
		<title>By Thor&#8217;s Hammer!</title>
		<link>http://yehudakatz.com/2008/05/12/by-thors-hammer/</link>
		<comments>http://yehudakatz.com/2008/05/12/by-thors-hammer/#comments</comments>
		<pubDate>Tue, 13 May 2008 01:54:00 +0000</pubDate>
		<dc:creator>wycats</dc:creator>
		
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://yehudakatz.com/?p=38</guid>
		<description><![CDATA[For the past few months, I&#8217;ve become more and more disillusioned by the current state of Ruby&#8217;s scripting support. Sure, we have optparse, and a gamut of other solutions, but there&#8217;s no full-stack package for writing robust binaries.
Enter thor.
The idea behind thor initially came from my work on a textmate binary (more on that later [...]]]></description>
			<content:encoded><![CDATA[<p>For the past few months, I&#8217;ve become more and more disillusioned by the current state of Ruby&#8217;s scripting support. Sure, we have optparse, and a gamut of other solutions, but there&#8217;s no full-stack package for writing robust binaries.</p>
<p>Enter <code>thor</code>.</p>
<p>The idea behind thor initially came from my work on a textmate binary (more on that later today), which would manage installed Textmate bundles. Sure, there&#8217;s the <a href="http://projects.validcode.net/getbundle">getbundle bundle</a>, and you can manually <a href="http://macromates.com/textmate/manual/bundles#getting_more_bundles">get bundles from subversion</a>, but I wanted a single binary that handled all of that.</p>
<p>While I was building it, I decided to map the commands that people could enter to a class and its methods. I created a small file that I included with the textmate binary called class_cli, and created some syntax for mapping things, like so (note that this is not the final syntax):</p>
<pre>
class MyApp
  include CLI

  desc "list [list]&#8220;, &#8220;list some items&#8221;
  def list(list = &#8220;stuff&#8221;)
    puts list.split(/,\s*/).join(&#8221;\n&#8221;)
  end
end

MyApp.start
</pre>
<p>Assuming the binary is called <code>app</code>, you could then do <code>app list "one,two,three"</code>, which would print:</p>
<pre>
one
two
three
</pre>
<p>In the case of the textmate binary, I created methods for <code>list</code>, <code>install</code>, <code>installed</code>, and <code>uninstall</code>.</p>
<p>This worked nicely, so I wanted to extract it out for use in other tools. I started the Hermes project on github, but while drinking with <a href="http://ozmm.org/">Chris Wanstrath</a>, I was convinced that the name <code>Thor</code> would be more appealing. Oh, the things that alcohol can do. I was also convinced of a couple other things (again, in a slightly inebriated state):</p>
<ul>
<li>rake and sake needed to be replaced <strong>for scripts</strong>, not as a replacement for make</li>
<li>nobody was going to use <s>hermes</s>thor if they need extra boilerplate like MyApp.start</li>
</ul>
<p>Long story short, Chris convinced me to make Thor a full-fledged scripting solution. So I did.</p>
<p>Thor, as it exists today, has two components:</p>
<ul>
<li>The Thor superclass, which works exactly like CLI/Hermes, except that you inherit from it instead of including it (class MyApp < Thor).</li>
<li>The Thor runner, which can run Thortasks that are in a local directory or installed from a remote location</li>
</ul>
<p>The thor runner allows you to make files like:</p>
<pre>
# module: random

class Amazing < Thor
  desc "describe NAME", "say that someone is amazing"
  method_options :forcefully => :boolean
  def describe(name, opts)
    ret = &#8220;#{name} is amazing&#8221;
    puts opts["forcefully"] ? ret.upcase : ret
  end

  desc &#8220;hello&#8221;, &#8220;say hello&#8221;
  def hello
    puts &#8220;Hello&#8221;
  end
end
</pre>
<p>If you call the file *.thor or Thorfile and place it in your current directory, any directory above you, or tasks/*.thor, you can then invoke the thorfile in any of the following ways:</p>
<pre>
$ thor -T
Tasks
-----
amazing:describe NAME [--forcefully]   say that someone is amazing
amazing:hello                          say hello

$ thor amazing:hello
Hello

$ thor amazing:describe &#8220;This blog reader&#8221;
This blog reader is amazing

$ thor amazing:describe &#8220;This blog reader&#8221; &#8211;forcefully
THIS BLOG READER IS AMAZING
</pre>
<p>You can also install local tasks or remote tasks to your system thor cache and make them available anywhere:</p>
<pre>
$ thor install task.thor
Your Thorfile contains:
# module: random

class Amazing < Thor
  desc "describe NAME", "say that someone is amazing"
  method_options :forcefully => :boolean
  def describe(name, opts)
    ret = &#8220;#{name} is amazing&#8221;
    puts opts["forcefully"] ? ret.upcase : ret
  end

  desc &#8220;hello&#8221;, &#8220;say hello&#8221;
  def hello
    puts &#8220;Hello&#8221;
  end
end
Do you wish to continue [y/N]? y
Storing thor file in your system repository

$ thor installed
Name      Modules
&#8212;-      &#8212;&#8212;-
random    amazing

Tasks
&#8212;&#8211;
amazing:describe NAME [--forcefully]   say that someone is amazing
amazing:hello                          say hello

$ thor amazing:hello
Hello

&#8230; same as above &#8230;
</pre>
<p>You can also specify a URL instead of a file name; like sake, thor uses open-uri to get the files. You uninstall or update thor modules based on the short name that was provided; if <code># module: name</code> exists at the top of the file, thor will use that by default. You can also use <code>thor install task.thor --as my_short_name</code>. If you don&#8217;t provide a short name, thor will ask for one.</p>
<p>Later, you can do <code>thor update short_name</code> and thor will remember where you got the module from and try to update it. <code>thor uninstall short_name</code> will remove the module from your list of installed modules.</p>
<p>Of course, <code>thor -T</code> (or <code>thor list</code>) will list local tasks <strong>and</strong> system-wide tasks in the resulting list, so you don&#8217;t need a separate tool to track your thortasks, and a local task can be made into a system task trivially.</p>
<p>Finally, thor itself is self-hosting; the thor runner uses the Thor superclass. As a result, I added some more features to the superclass as I built the runner:</p>
<ul>
<li>You can map short names to their full name: <code>map "-T" =&gt; :list</code>, which is why <code>thor -T</code> and <code>thor list</code> are identical</li>
<li>You can provide additional options that get passed in as a Hash (<code>method_options :as =&gt; :required</code>). The available option types are <code>:required</code>, <code>:optional</code>, and <code>:boolean</code>. The resulting hash is passed in as the final parameter to your method and the pretty-printed help automatically includes them in the usage screen.</li>
</ul>
<p>Take a look at the <a href="http://github.com/wycats/thor/tree/master">thor repository on github</a>, and specifically, <a href="http://github.com/wycats/thor/tree/master/bin/thor">the thor runner</a> for more information on how it all fits together.</p>
]]></content:encoded>
			<wfw:commentRss>http://yehudakatz.com/2008/05/12/by-thors-hammer/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Best Things Come in Threes</title>
		<link>http://yehudakatz.com/2008/05/05/best-things-come-in-threes/</link>
		<comments>http://yehudakatz.com/2008/05/05/best-things-come-in-threes/#comments</comments>
		<pubDate>Mon, 05 May 2008 17:32:17 +0000</pubDate>
		<dc:creator>wycats</dc:creator>
		
		<category><![CDATA[Other]]></category>

		<guid isPermaLink="false">http://yehudakatz.com/2008/05/05/best-things-come-in-threes/</guid>
		<description><![CDATA[I was at the Dallas Tech Fest this past weekend, and had the opportunity to meet up with some cool guys in the DataMapper community (Sam, Adam, Ben and Bryan). While I was there, I ended up hacking out a few unrelated tools that I thought I&#8217;d share with the community.
benchwarmer
Benchwarmer is an improved DSL [...]]]></description>
			<content:encoded><![CDATA[<p>I was at the Dallas Tech Fest this past weekend, and had the opportunity to meet up with some cool guys in the DataMapper community (Sam, Adam, Ben and Bryan). While I was there, I ended up hacking out a few unrelated tools that I thought I&#8217;d share with the community.</p>
<h3>benchwarmer</h3>
<p>Benchwarmer is an improved DSL for doing benchmarks (hat tip for the name to <a href="http://geeksomnia.com">br0nette</a>). It provides options for grouping, and produces output like:</p>
<pre>
Running the benchmarks 100000 times each...

                         Option 1 |   TWO | Option 3 |
 -----------------------------------------------------
 Squeezing with #squeeze     0.15 |  0.15 |     0.14 |
              with #gsub     0.38 |  0.35 |     0.36 |
 -----------------------------------------------------
 Spliting    with #split     0.43 |  0.51 |     0.61 |
             with #match     0.29 |  0.35 |     0.38 |
 -----------------------------------------------------
</pre>
<p>You get that output by doing:</p>
<pre>
  Benchmark.warmer(TIMES) do
    columns :one, :two, :three
    titles :one => "Option 1", :three => "Option 3"      

    group("Squeezing") do
      report "with #squeeze" do
        one { "abc//def//ghi//jkl".squeeze("/") }
        two { "abc///def///ghi///jkl".squeeze("/") }
        three { "abc////def////ghi////jkl".squeeze("/") }
      end
      report "with #gsub" do
        one { "abc//def//ghi//jkl".gsub(/\/+/, "/") }
        two { "abc///def///ghi///jkl".gsub(/\/+/, "/") }
        three { "abc////def////ghi////jkl".gsub(/\/+/, "/") }
      end
    end

    group("Spliting") do
      report "with #split" do
        one { "aaa/aaa/aaa.bbb.ccc.ddd".split(".") }
        two { "aaa//aaa//aaa.bbb.ccc.ddd.eee".split(".") }
        three { "aaa///aaa///aaa.bbb.ccc.ddd.eee.fff".split(".") }
      end
      report "with #match" do
        one { "aaa/aaa/aaa.bbb.ccc.ddd".match(/\.([^\.]*)$/) }
        two { &#8220;aaa//aaa//aaa.bbb.ccc.ddd.eee&#8221;.match(/\.([^\.]*)$/) }
        three { &#8220;aaa///aaa///aaa.bbb.ccc.ddd.eee.fff&#8221;.match(/\.([^\.]*)$/) }
      end
    end
  end
</pre>
<p>Most of that is optional; you can get usable benchmarks by stripping the DSL down to:</p>
<pre>
  Benchmark.warmer(TIMES) do
    report "squeezing with #squeeze" do
      "abc//def//ghi//jkl".squeeze("/")
    end
    report "squeezing with #gsub" do
      "abc//def//ghi//jkl".gsub(/\/+/, "/")
    end
  end
</pre>
<p>which produces:</p>
<pre>

                         Results |
----------------------------------
 squeezing with #squeeze    0.15 |
    squeezing with #gsub    0.34 |
----------------------------------
</pre>
<p>It is available at <a href="http://github.com/wycats/benchwarmer/tree/master">github</a>. I will add the appropriate stuff so you can do<br />
gem install wycats-benchwarmer. For now, you can just check out the git repo and do <code>rake install</code>.</p>
<p>I extracted this out of the benchmarks I was doing as I was building&#8230;</p>
<h3>10x Faster Rails and Merb Inflector</h3>
<p>On the plane over to Dallas Tech Fest, I thought it would be nice to try and improve the performance of the Rails Inflector, which is currently pretty slow (albeit probably not a bottleneck). Merb already uses the Facets English Inflector, which is 2x or so faster, but I was pretty sure I could do even better.</p>
<p>When I analyzed the English Inflector, I noticed a few things:</p>
<ul>
<li>They were doing something similar to Rails looping over a list of regexen and picking the correct ones</li>
<li>Without exception, the correct choice was the longest string that matched the end of the word.</li>
<li>Matching a string (like fooses) against a regex with (foo|foos|fooses) will always match the longest string</li>
<li>Neither Rails nor English were caching the resulting words, which don&#8217;t change, and in the case of Inflecting Rails models and controllers, are a small universe of total words</li>
</ul>
<p>As a result, I did two optimizations:</p>
<ul>
<li>I packed all of the regexen into a single regex, and got rid of the sort-by-longest-string code. I then did a simple sub! against the word, pulling the results out of the rules Hash (that already existed in English)</li>
<li>English already cached irregular words (its first step was to look in the irregular words hash for the word in question), so I simply extended this cache to include any word already found.</li>
</ul>
<p>Between these two optimizations, I was able to get around 10x over Rails, and got a huge boost for simple pluralization words (not so much for things like &#8220;person&#8221; => &#8220;people&#8221;). Rails also caught a bunch of cases in their tests that were not supported by English, so I added support for things like capital versions (&#8221;Person&#8221; => &#8220;People&#8221;) and partial words (&#8221;foo_child&#8221; => &#8220;foo_children&#8221;).</p>
<p>Here are the benchwarmer results:</p>
<pre>
                                         OLD  | NEW   | RAILS
Simple: account => accounts    Singular  0.16 |  0.02 |  0.25
                                 Plural  0.14 |  0.03 |  0.24
-------------------------------------------------------------
Simple: American => Americans  Singular  0.16 |  0.02 |  0.27
                                 Plural  0.16 |  0.31 |  0.27
-------------------------------------------------------------
Abnormal: dwarf => dwarves     Singular  0.07 |  0.04 |  0.17
                                 Plural  0.06 |  0.03 |  0.17
-------------------------------------------------------------
Abnormal: hero => heroes       Singular  0.05 |  0.03 |  0.20
                                 Plural  0.06 |  0.02 |  0.21
-------------------------------------------------------------
One Way: cactus => cactuses    Singular  0.11 |  0.02 |  0.26
                                 Plural  0.07 |  0.02 |  0.26
-------------------------------------------------------------
One Way: wife => wives         Singular  0.11 |  0.02 |  0.16
                                 Plural  0.13 |  0.03 |  0.14
-------------------------------------------------------------
Uncountable: fish => fish      Singular  0.03 |  0.02 |  0.03
                                 Plural  0.02 |  0.02 |  0.03
-------------------------------------------------------------
Exception: person => people    Singular  0.02 |  0.03 |  0.09
                                 Plural  0.03 |  0.02 |  0.11
-------------------------------------------------------------
</pre>
<p>It does break back-compat a bit with Rails, as the mechanism for adding new rules is simpler in order to be compatible with English&#8217;s faster inflector algorithm. I also had to remove Inflector#clear, which I wasn&#8217;t sure anyone was actually using (it allowed the clearing of very specific types of rules, which can no longer be supported as irregular words and regular singularization/pluralization rules are dumped in the same list for efficiency).</p>
<p>The new way of defining rules is:</p>
<pre>
Inflector.inflections do
  # One argument means singular and plural are the same.

  word 'equipment'
  word 'information'
  word 'money'
... snip ...
  word 'Swiss'     , 'Swiss'
  word 'virus'     , 'viri'
  word 'octopus'   , 'octopi'
... snip ...
  rule 'person' , 'people', true
  rule 'shoe'   , 'shoes', true
  rule 'hive'   , 'hives', true
  rule 'man'    , 'men', true
  rule 'rf'     , 'rves'
  rule 'ero'    , 'eroes'
... snip ...
  singular_rule 'of' , 'ofs' # proof
  singular_rule 'o'  , 'oes' # hero, heroes
... snip ...
  plural_rule 's'   , 'ses'
  plural_rule 'ive' , 'ives' # don't want to snag wife
  plural_rule 'fe'  , 'ves'  # don't want to snag perspectives
</pre>
<p>All of the inflector tests still pass in Rails, with the exception of the #clear tests, which were too coupled to the old implementation to salvage (they actually introspected into specific ivars).</p>
<p>The Rails modifications are available on my <a href="http://github.com/wycats/rails/tree/master">Rails branch on github</a>. I hope to be able to push<br />
the changes upstream if the Rails core folks are amenable :).</p>
<h3>OSX Window Resizing Tools</h3>
<p>I was going crazy trying to figure out a way to cordon off a section of my screen for screencasts (the center 800&#215;600 for instance), and ended up in crazy AppleScript-land. I ended up with two useful scripts:</p>
<ul>
<li>center, which takes any window, resizes it to a provided size, and centers it on the screen</li>
<li>maximize, which takes any window and maximizes it to fill the screen (even for crazy-zoom-windows like Safari)</li>
</ul>
<p>Both scripts take drawers into consideration, so you can resize a window like TextMate to the correct size.</p>
<p>It is available at <a href="http://github.com/wycats/osx-window-sizing/tree/master">github</a> and comes with a convenient raketask for installing<br />
that will compile and copy your scripts into the appropriate folders. It also comes with a raketask that will install FastScripts Lite, which will<br />
allow you to bind keys to these scripts (see the README for full details).</p>
]]></content:encoded>
			<wfw:commentRss>http://yehudakatz.com/2008/05/05/best-things-come-in-threes/feed/</wfw:commentRss>
		</item>
		<item>
		<title>IE8 is Gonna Rock</title>
		<link>http://yehudakatz.com/2008/03/21/ie8-is-gonna-rock/</link>
		<comments>http://yehudakatz.com/2008/03/21/ie8-is-gonna-rock/#comments</comments>
		<pubDate>Fri, 21 Mar 2008 13:10:56 +0000</pubDate>
		<dc:creator>wycats</dc:creator>
		
		<category><![CDATA[Other]]></category>

		<guid isPermaLink="false">http://yehudakatz.com/2008/03/21/ie8-is-gonna-rock/</guid>
		<description><![CDATA[So maybe my title was a bit overdramatic. But in the announcement of IE8&#8217;s features, one jumped out at me as a potentially game-changing feature.
http://www.microsoft.com/windows/products/winfamily/ie/ie8/readiness/DevelopersNew.htm#dom
Yes.
That&#8217;s right.
&#8220;Selectors are a query language for searching and “selecting” tags (elements) within a webpage. They are most commonly seen in CSS to “select” a group of elements to which certain [...]]]></description>
			<content:encoded><![CDATA[<p>So maybe my title was a bit overdramatic. But in the announcement of IE8&#8217;s features, one jumped out at me as a potentially game-changing feature.</p>
<p>http://www.microsoft.com/windows/products/winfamily/ie/ie8/readiness/DevelopersNew.htm#dom</p>
<p>Yes.</p>
<p>That&#8217;s right.</p>
<p>&#8220;Selectors are a query language for searching and “selecting” tags (elements) within a webpage. They are most commonly seen in CSS to “select” a group of elements to which certain properties will be applied:</p>
<pre>
Selector{
   property: value;
   property2: value;
}
</pre>
<p>In Internet Explorer 7 there is no way of &#8220;executing&#8221; the selector independently of CSS. Internet Explorer 8’s implementation of the Selectors API is based on the W3C Working Draft: http://www.w3.org/TR/selectors-api/&#8221;</p>
<p>Yes. You read that correctly. To the extent of IE8&#8217;s support for CSS2.1 (which should be relatively complete, according to their announcement), it will be possible to use the standards-compliant <code>querySelector</code> and <code>querySelectorAll</code> in IE to get collections of elements at native speed. This means fast class-name lookup, fast attribute lookup, and much much more.</p>
<p>Here&#8217;s a lame example of its use from an IE8 whitepaper:</p>
<pre>function doValidation ()  {
// Retrieve the required elements by using Selectors
   // Selects all the form fields with 'required' classes
var reqs = document.querySelectorAll(".required");
   // Set the flag to false by default
var missingRequiredField = false;
// Validate that the form data is not empty
for (var i = 0; i < reqs.length; i++)  {
if (reqs[i].value == "")
         missingRequiredField = true;
   }</pre>
<p>Yep. It is what it looks like. Finally, the days of slow IE selectors may be coming to an end. And finally, one where MS is ahead of the curve (in fact, a lot of the IE8 improvements are pretty neat and forward-looking; I recommend <a href="http://www.microsoft.com/windows/products/winfamily/ie/ie8/readiness/DevelopersNew.htm">taking a look at it</a>)</p>
]]></content:encoded>
			<wfw:commentRss>http://yehudakatz.com/2008/03/21/ie8-is-gonna-rock/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Modularity in jQuery: Part II</title>
		<link>http://yehudakatz.com/2008/02/05/modularity-in-jquery-part-ii/</link>
		<comments>http://yehudakatz.com/2008/02/05/modularity-in-jquery-part-ii/#comments</comments>
		<pubDate>Tue, 05 Feb 2008 07:37:30 +0000</pubDate>
		<dc:creator>wycats</dc:creator>
		
		<category><![CDATA[Other]]></category>

		<guid isPermaLink="false">http://yehudakatz.com/2008/02/05/modularity-in-jquery-part-ii/</guid>
		<description><![CDATA[Last time, we talked about using event bindings to provide a publish/subscribe mechanism for your plugins, and introduce modularity via the event system.
This time, we&#8217;ll talk about two new features added since jQuery 1.2: Setup and teardown for events and the element data store. First up, the element data store:
Prior to jQuery 1.2, data was [...]]]></description>
			<content:encoded><![CDATA[<p>Last time, we talked about using event bindings to provide a publish/subscribe mechanism for your plugins, and introduce modularity via the event system.</p>
<p>This time, we&#8217;ll talk about two new features added since jQuery 1.2: Setup and teardown for events and the element data store. First up, the element data store:</p>
<p>Prior to jQuery 1.2, data was added to elements via expandos. If, for instance, <code>d</code> was an element, and we wanted to add some data named &#8220;foo&#8221; with the value &#8220;bar&#8221;, we would do <code>d.foo = "bar"</code>. Simple enough. But there are two major issues:</p>
<ul>
<li>Other libraries, plugins, or widgets might try and use the foo name. We could try and obfuscate the name (jQuery 1.0 used $events as the expando name for the events array on an element), but there was still a chance that another library could accidentally use the same name. In fact, that&#8217;s exactly what happened. MooTools inadvertently used $events, and people who tried to use both libraries got hosed.</li>
<li>IE&#8217;s garbage collection gets all flummoxed under certain circumstances when an element is removed but its expandos still have references to live objects.</li>
</ul>
<p>The solution, which jQuery adopted for 1.2, is to create a new data store that&#8217;s independent of the element and in the jQuery namespace, and which jQuery garbage collects itself when you remove the associated elements. This provides a mechanism for tagging elements with information that can then be used by different modules in your program. While jQuery uses it to track which events belong to which elements, it can also be used for other purposes.</p>
<p>For instance, the jQuery metadata plugin allows you to specify data to be stored in the data store in your markup. Here&#8217;s an example:</p>
<h3>The markup</h3>
<pre>
&lt;div data="{foo: 'bar'}"&gt;Some div element is here&lt;/div&gt;
</pre>
<h3>The code</h3>
<pre>
$.metadata.setType("attr", "data");
$("div").metadata().foo == "bar"
</pre>
<p>The metadata plugin produces a thin sheen over the data-store, allowing you to pass information into the store via markup. That might be quite useful for plugins, to allow your users to specify options for your plugin without needing to write any special code. For instance, you could write this code in your plugin:</p>
<h3>Markup</h3>
<pre>
&lt;div class="myPlugin" data="{myPlugin: {option1: 12, option2: "stringyString"}}"&gt;Some Text&lt;/div&gt;
</pre>
<h3>The Code</h3>
<pre>
$.metadata.setType("attr", "data");
jQuery(function($) {
  $("div.myPlugin").each(function() {
    $(this).myPlugin($(this).metadata().myPlugin);
  });
});
</pre>
<p>This is assuming, of course, that there is some $.fn.myPlugin that takes option1 and option2 as options. This technique allows you to put the initialization code in your plugin itself and let the user interact with it in fairly high-level ways via markup. The TableSorter plugin makes use of this technique.</p>
<p>But you can use the data store for other purposes as well. In particular, you can interact directly with the data store by doing $.data(el, &#8220;key&#8221;, &#8220;value&#8221;) to set a key in the store, or $.data(el, &#8220;key&#8221;) to retrieve a key. jQuery 1.2.3 will also add a new shortcut (which we&#8217;ll discuss in more length in the next post in this series), which will allow $(&#8221;div&#8221;).data(&#8221;key&#8221;).</p>
<p>I use this technique in the autocomplete plugin to tag a keystroke as &#8220;final&#8221; (hitting escape, for instance, should not trigger a new dropdown if it&#8217;s used to cancel the autocomplete). Even though the autocompleter is in a separate module from the event code that triggers the autocompleter, this information can be tagged onto the element so it&#8217;s accessible from any module.</p>
<h2>Setup and Teardown</h2>
<p>Setup and teardown lets you further customize event binding. In effect, setup and teardown functions are called immediately before an event is bound (and immediately before an event is unbound), allowing you to preform processing. Returning true from the setup function causes it to get bound using the browser&#8217;s native handlers, instead of the jQuery event system (don&#8217;t worry about this; it&#8217;s extremely rare). Let&#8217;s take a hypothetical <code>activate.myPlugin</code> event.</p>
<h3>The Code</h3>
<pre>
$.event.special["activate.myPlugin] = function() {
  /* &#8220;this&#8221; is the element being bound */
  /* do some processing */;
}
</pre>
<p>Later, when you do <code>$("div").bind("activate.myPlugin")</code> the special method will get run. This allows your plugin&#8217;s users to add some processing to when your plugin gets bound to an element, without having to override the plugin binding method.</p>
<p><b>Next up:</b> New features that will be introduced in 1.2.3, and bringing this all together :)</p>
]]></content:encoded>
			<wfw:commentRss>http://yehudakatz.com/2008/02/05/modularity-in-jquery-part-ii/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Modularity in jQuery</title>
		<link>http://yehudakatz.com/2008/02/01/modularity-in-jquery/</link>
		<comments>http://yehudakatz.com/2008/02/01/modularity-in-jquery/#comments</comments>
		<pubDate>Sat, 02 Feb 2008 00:35:22 +0000</pubDate>
		<dc:creator>wycats</dc:creator>
		
		<category><![CDATA[Other]]></category>

		<guid isPermaLink="false">http://yehudakatz.com/2008/02/01/modularity-in-jquery/</guid>
		<description><![CDATA[There have recently been several comments along the lines that jQuery is not very modular, once you get past modifying DOM elements. I get why people are saying that, but there&#8217;s been a lot of features added to jQuery over the past year or so that provide a compelling architecture for building modular apps.
It&#8217;s not [...]]]></description>
			<content:encoded><![CDATA[<p>There have recently been several comments along the lines that jQuery is not very modular, once you get past modifying DOM elements. I get why people are saying that, but there&#8217;s been a lot of features added to jQuery over the past year or so that provide a compelling architecture for building modular apps.</p>
<p>It&#8217;s not exactly OO, which is what a lot of people are used to; it&#8217;s more like a publish/subscribe model, with points of modularity added via message passing. That&#8217;s because jQuery provides a pretty kick-ass system for custom events (events that are not built-into the browser), including a mechanism to subscribe to such events and a mechanism to trigger (i.e. publish) the events, with custom data.</p>
<p>It also provides the potential for setup and teardown for both browser and custom events, as well as a central data store for persistent data that would traditionally be attached directly to an element. This solves both the memory-leak issue (jQuery manages garbage collection of objects in the store that are no longer valid because the DOM element doesn&#8217;t exist anymore) as well as a crucial compatibility issue (we don&#8217;t store the data in the element itself, so we work fine with other libraries that do, or other libraries that have their own store in their own namespace).</p>
<h2>Events</h2>
<p>If you use jQuery, you already know about browser events:</p>
<pre>$("div").click(function() { /* code here */ });</pre>
<p>That&#8217;s a shortcut for:</p>
<pre>$("div").bind("click", function() { /* code here */ });</pre>
<p>What you probably don&#8217;t know is that you can also bind your own custom events:</p>
<pre>$("div").bind("clickeroo", function() { /* code here */ });</pre>
<p>That probably doesn&#8217;t seem all that useful until you realize you can also trigger these custom events:</p>
<pre>$("div").trigger("clickeroo")</pre>
<p>or even:</p>
<pre>$("div").bind("clickeroo", function(e, data1, data2) {
  // some stuff that uses data
});
$("div").trigger("clickeroo", [dataObject1, dataObject2]);</pre>
<p>And finally, you can bind custom events with namespaces:</p>
<pre>$("div").bind("activate.dialog", function(event, options) {
  // some stuff that uses options
});</pre>
<p>Triggering <code>activate</code> or <code>activate.dialog</code> will trigger the event, and because all JS arguments are optional, the options argument will work whether or not the core event was triggered with the additional data.</p>
<h2>Where does this bring us?</h2>
<p>So I claimed earlier that this provides us with a useful tool for modularity. If you&#8217;ve been paying attention, and like me, have run into some modularity ceilings, you should be realizing some of the possibilities here. Effectively, what you have is a publish/subscribe model for events. If I create a dialog plugin, I don&#8217;t have to have a massive options hash to provide a callback for the dialog opening, the dialog closing, the dialog resizing, etc. All I need to do is blindly trigger <code>open.dialog</code>, <code>close.dialog</code>, <code>resize.dialog</code>, etc. and allow the user to subscribe to those events if necessary. This even allows for meta-plugins that simply subscribe to events published by the main plugin and extend them. This is the approach I take in my autocomplete plugin, which publishes &#8220;autocomplete.activate&#8221;, &#8220;autocomplete.cancel&#8221;, and &#8220;autocomplete.selected&#8221; events.</p>
<p>My plugin also subscribes to the &#8220;updateList.autocomplete&#8221; event, which allows outside code to provide a list for the autocompleter at any time, without having to know how the internal autocomplete code works. In fact, the Ajax extension to the autocomplete plugin simply registers a callback for the call to getList that the plugin calls, which publishes an updateList.autocomplete event with the appropriate data when done.</p>
<p>In the end, this allows you to create self-contained modules in your application that communication with each other by providing public publish and subscribe points for interaction. In the case of the autocomplete plugin, all the events are published onto the input element, and external interfaces subscribe to events published to that element. This allows multiple autocomplete plugins on a single page that don&#8217;t need to use a single message bus. However, for more global events, you can publish/subscribe to/from the window or document object, which can provide a more global event bus.</p>
<p><b>Next time</b>, I&#8217;ll talk about the setup and teardown methods for custom events, as well as using the element data store to store data about an element that needs to be shared between modules.</p>
]]></content:encoded>
			<wfw:commentRss>http://yehudakatz.com/2008/02/01/modularity-in-jquery/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Merb.next</title>
		<link>http://yehudakatz.com/2008/01/14/merbnext/</link>
		<comments>http://yehudakatz.com/2008/01/14/merbnext/#comments</comments>
		<pubDate>Mon, 14 Jan 2008 20:20:02 +0000</pubDate>
		<dc:creator>wycats</dc:creator>
		
		<category><![CDATA[Other]]></category>

		<guid isPermaLink="false">http://yehudakatz.com/2008/01/14/merbnext/</guid>
		<description><![CDATA[No, ladies and gentlemen, we haven&#8217;t taken a break after releasing 0.5 (The &#8220;Thanks Zed&#8221; release). With 0.5 out, stable and fast, the Merb team finally took a hard look at where we&#8217;re going with the framework. Thanks to Engine Yard&#8217;s investment in our work, I was able to meet with Ezra in person and [...]]]></description>
			<content:encoded><![CDATA[<p>No, ladies and gentlemen, we haven&#8217;t taken a break after releasing 0.5 (The &#8220;Thanks Zed&#8221; release). With 0.5 out, stable and fast, the Merb team finally took a hard look at where we&#8217;re going with the framework. Thanks to <a href="http://www.engineyard.com">Engine Yard</a>&#8217;s investment in our work, I was able to meet with Ezra in person and spent a large part of last week planning out Merb.next.</p>
<p>First up, we&#8217;re closing in on Merb 1.0. The next major release will be numbered 0.9, and will be the point release immediately prior to 1.0. In order to help make Merb even more modular and tight, Merb 0.9 will be broken up into a merb-core gem and a package of merb-more gems. The entire package will still install via gem install merb, and will be fully maintained by the core team. However, in Merb.next, it&#8217;ll be trivial to leave off chunks of functionality that you don&#8217;t want.</p>
<p>Merb Core, which we&#8217;re working on right now (you can follow our progress at <a href="http://github.com/~wycats/merb-core/tree/master">Git Hub</a>), will be the tight core of the framework, just enough to run basic web services and simple web sites. It&#8217;ll be possible to use Merb Core for a single-page app (a la Camping or Sinatra) or an app with a full-blown application structure (like Rails or current Merb).</p>
<p>Merb 0.9 will also tie up a bunch of loose ends, not shying away from breaking things in order to give Merb 1.0&#8217;s API as much longevity as possible. At the same time, we&#8217;re trying to make the upgrade path from Merb.current to Merb.next as painless as possible.</p>
<p>In an effort to keep Merb core really tight, we&#8217;re also adopting some more formal programming and documentation techniques that will make working on and using Merb much easier:</p>
<ul>
<li>All methods (public and private) are required to provide a clear method signature, including the types for any parameters, and the possible values for any options hashes, as well as return types and other information (see below for an example).</li>
<li>Methods can be tagged @public, which means that changing them will break the public API. Methods can also be tagged @semipublic, which means that they&#8217;re used in other parts of the framework and possibly the test harness. This&#8217;ll aid future refactoring, because methods that are not public or semipublic can be reimplemented at will as long as the test suite still passes.</li>
<li>We&#8217;re removing all hardcoded paths from the framework. This means that Merb will scale from a single-page app to any application structure you want. You can even specify how to name template files (you can go from the traditional controller/view.mime.type or controller.view.mime.type or even view.mime.type for a single controller app)</li>
<li>We&#8217;re splitting our test suite into public and private tests. Public tests test the public API, while private tests test things like the Request object. Public tests are required to use only methods in the public or semipublic API, so they should still pass after major refactors.</li>
</ul>
<p>Merb Core is also based on a Rack adapter now, so it&#8217;ll work out of the box with Mongrel, Evented Mongrel, FCGI, regular CGI, thin, webrick, and Fuzed, as well as any server you can write a rack adapter for. And fear not, you&#8217;ll still have access to the raw mongrel request if you want to do fancy stuff like streaming or deferred rendering outside the mutex.</p>
<p>Here&#8217;s an example of the new documentation standards:</p>
<pre>
  # Render the specified item, with the specified options.
  #
  # ==== Parameters
  # thing&lt;String, Symbol, nil&gt;::
  #   The thing to render. This will default to the current action
  # opts&lt;Hash&gt;:: An options hash (see below)
  #
  # ==== Options (opts)
  # :format&lt;Symbol&gt;:: A registered mime-type format
  # :template&lt;String&gt;::
  #   The path to the template relative to the template root
  # :status<~to_i>::
  #   The status to send to the client. Typically, this would
  #   be an integer (200), or a Merb status code (Accepted)
  # :layout<~to_s>::
  #   A layout to use instead of the default. This should be
  #   relative to the layout root. By default, the layout will
  #   be either the controller_name or application. If you
  #   want to use an alternative content-type than the one
  #   that the base template was rendered as, you will need
  #   to do :layout => &#8220;foo.#{content_type}&#8221; (i.e. &#8220;foo.json&#8221;)
  #
  # ==== Returns
  # String:: The rendered template, including layout, if appropriate.
  #
  # ==== Raises
  # TemplateNotFound::
  #   There is no template for the specified location.
  #
  # ==== Alternatives
  # If you pass a Hash as the first parameter, it will be moved to
  # opts and &#8220;thing&#8221; will be the current action
  #
  #&#8212;
  # @public
  def render(thing = nil, opts = {})
    &lt;snip&gt;
  end
</pre>
<p>Some description: The render method takes two parameters, a &#8220;thing&#8221; to render and an options Hash. The &#8220;thing&#8221; can be a String, Symbol, or nil. The options hash takes :format, :template, :status, and :layout options, which are described under Options. Th :status option must respond_to? to_i and the :layout options must respond_to? to_s. It returns a String (the rendered layout), and raises a TemplateNotFound if the template specified is not found. It is part of the @public API.</p>
<p>The documentation format will be itself documented as part of the Merb Core release, and we hope to write a parser that uses RDoc to spit out just the public API (leaving out public methods that are not marked @public).</p>
<p>The public test suite itself is also designed to provide a useful starting point for Merb plugin authors; here&#8217;s an example:</p>
<pre>
  # @public
  it "should accept template-type registrations via #register_extensions" do
    Merb::Template.register_extensions(Merb::Test::Fixtures::MyTemplateEngine, %w[myt])
    Merb::Template.engine_for(&#8221;foo.myt&#8221;).should == Merb::Test::Fixtures::MyTemplateEngine
  end
</pre>
<p>All in all, it&#8217;s going to be damn tight, formal, and grokkable. I plan to release some code traces once 0.9 is done that trace the path of a request through the Merb framework, so you can grok exactly how requests go through the pipeline and are eventually rendered.</p>
<p>Good Coding and Good Luck!</p>
]]></content:encoded>
			<wfw:commentRss>http://yehudakatz.com/2008/01/14/merbnext/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Almost There: 30% Off jQuery in Action!</title>
		<link>http://yehudakatz.com/2007/12/18/almost-there-30-off-jquery-in-action/</link>
		<comments>http://yehudakatz.com/2007/12/18/almost-there-30-off-jquery-in-action/#comments</comments>
		<pubDate>Tue, 18 Dec 2007 23:29:12 +0000</pubDate>
		<dc:creator>wycats</dc:creator>
		
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://yehudakatz.com/2007/12/18/almost-there-30-off-jquery-in-action/</guid>
		<description><![CDATA[As you might know, I&#8217;m putting the final touches on jQuery in Action, an introductory book on all things jQuery for Manning Publications. I&#8217;ve spent the last half a year diligently plugging away at it, along with my co-author Bear Bibeault, and we&#8217;re finally hitting the home stretch.
It&#8217;s taken a lot of time, and focus, [...]]]></description>
			<content:encoded><![CDATA[<p>As you might know, I&#8217;m putting the final touches on <a href="http://www.manning.com/affiliate/idevaffiliate.php?id=485_93">jQuery in Action</a>, an introductory book on all things jQuery for Manning Publications. I&#8217;ve spent the last half a year diligently plugging away at it, along with my co-author Bear Bibeault, and we&#8217;re finally hitting the home stretch.</p>
<p>It&#8217;s taken a lot of time, and focus, and while it&#8217;s been a great experience, I&#8217;m also looking forward to being able to once again put my full energy into the various Open Source projects I&#8217;m involved in (including <a href="http://jquery.com">jQuery</a>, of course). We&#8217;re reviewing typeset PDF&#8217;s of what looks to be just about ready to print &#8212; there&#8217;s light at the end of this tunnel!</p>
<p>The book is set to print in early to mid-January; as a final push before publication, Manning is offering 30% off until December 31st, with the code JQM30. Though the book is also available on <a href="http://www.amazon.com/gp/redirect.html?ie=UTF8&#038;location=http%3A%2F%2Fwww.amazon.com%2FjQuery-Action-Bear-Bibeault%2Fdp%2F1933988355%3Fie%3DUTF8%26s%3Dbooks%26qid%3D1198020280%26sr%3D8-1&#038;tag=kagoyoto-20&#038;linkCode=ur2&#038;camp=1789&#038;creative=9325">Amazon.com</a><img src="http://www.assoc-amazon.com/e/ir?t=kagoyoto-20&amp;l=ur2&amp;o=1" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></a>, among other places, Manning is the only place you can also get the e-book, which at least for me, is really important.</p>
<p>Thanks to everyone who&#8217;s been there for me in this crazy busy time, and for everyone who&#8217;s had to grin and bear it while my focus was split. I&#8217;m almost done!</p>
]]></content:encoded>
			<wfw:commentRss>http://yehudakatz.com/2007/12/18/almost-there-30-off-jquery-in-action/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Search in a Directory (Third time&#8217;s the Charm)</title>
		<link>http://yehudakatz.com/2007/12/18/search-in-a-directory-third-times-the-charm/</link>
		<comments>http://yehudakatz.com/2007/12/18/search-in-a-directory-third-times-the-charm/#comments</comments>
		<pubDate>Tue, 18 Dec 2007 19:49:30 +0000</pubDate>
		<dc:creator>wycats</dc:creator>
		
		<category><![CDATA[Other]]></category>

		<guid isPermaLink="false">http://yehudakatz.com/2007/12/18/search-in-a-directory-third-times-the-charm/</guid>
		<description><![CDATA[So for all two of you who have been using my Search in a Directory bundle, I have a new version, that has been substantially improved:

a massive rewrite that successfully handles closing the window during a grep
some UI tweaks
an all-around improvement to the underlying code

I am also in the process of adding support for skipping [...]]]></description>
			<content:encoded><![CDATA[<p>So for all two of you who have been using my Search in a Directory bundle, I have a new version, that has been substantially improved:</p>
<ul>
<li>a massive rewrite that successfully handles closing the window during a grep</li>
<li>some UI tweaks</li>
<li>an all-around improvement to the underlying code</li>
</ul>
<p>I am also in the process of adding support for skipping certain directories (the current version skips .svn directories by default, but I want you to be able to turn on and off skipping Rails directories).</p>
<p>Download <a href="http://www.yehudakatz.com/wp-content/uploads/Wycats.tmbundle.zip">the bundle</a> today!</p>
]]></content:encoded>
			<wfw:commentRss>http://yehudakatz.com/2007/12/18/search-in-a-directory-third-times-the-charm/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Better Threading in Mail.app</title>
		<link>http://yehudakatz.com/2007/11/19/better-threading-in-mailapp/</link>
		<comments>http://yehudakatz.com/2007/11/19/better-threading-in-mailapp/#comments</comments>
		<pubDate>Mon, 19 Nov 2007 17:08:37 +0000</pubDate>
		<dc:creator>wycats</dc:creator>
		
		<category><![CDATA[Other]]></category>

		<guid isPermaLink="false">http://yehudakatz.com/2007/11/19/better-threading-in-mailapp/</guid>
		<description><![CDATA[I really like the new Mail.app in Leopard. With Gmail moving to IMAP, I was finally able to get virtually all of the functionality that I&#8217;m used to in Gmail on my desktop Mail application, which is really cool.
Well&#8230; except for one little thing. Mail.app has threads, but it does not thread Sent Mail in [...]]]></description>
			<content:encoded><![CDATA[<p>I really like the new Mail.app in Leopard. With Gmail moving to IMAP, I was finally able to get virtually all of the functionality that I&#8217;m used to in Gmail on my desktop Mail application, which is really cool.</p>
<p>Well&#8230; except for one little thing. Mail.app has threads, but it does <b>not</b> thread Sent Mail in with Received mail. In Gmail, on the other hand, inbound email gets threaded with outbound email, so I get a nice breakdown of the actual discussions I&#8217;ve been having. Devoid of outbound context, the threads are frequently confusing or meaningless.</p>
<p>The workaround I came up with is to create a new Smart Folder that includes Inbound email and Outbound email (Inbox and Sent Items). The Smart Mailbox, in threaded mode, correctly handles putting all of the email together. The little caveat is that outbound email that was not a reply to something appears in this view too (it does not appear in Gmail), but I think I actually prefer that mode of operating.</p>
]]></content:encoded>
			<wfw:commentRss>http://yehudakatz.com/2007/11/19/better-threading-in-mailapp/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Textmate Search in a Folder (with grep) Part Deux</title>
		<link>http://yehudakatz.com/2007/11/15/textmate-search-in-a-folder-with-grep-part-deux/</link>
		<comments>http://yehudakatz.com/2007/11/15/textmate-search-in-a-folder-with-grep-part-deux/#comments</comments>
		<pubDate>Thu, 15 Nov 2007 23:32:45 +0000</pubDate>
		<dc:creator>wycats</dc:creator>
		
		<category><![CDATA[TextMate]]></category>

		<guid isPermaLink="false">http://yehudakatz.com/2007/11/15/textmate-search-in-a-folder-with-grep-part-deux/</guid>
		<description><![CDATA[I posted my initial &#8220;Search in a Folder&#8221; solution a couple of days ago, and it turned out it had a few bugs (like the backgrounded ruby script never closing, and thus spawning a new ruby process for each time you loaded up the window. There were other user-experience annoyances, too. The good news is [...]]]></description>
			<content:encoded><![CDATA[<p>I posted my initial &#8220;Search in a Folder&#8221; solution a couple of days ago, and it turned out it had a few bugs (like the backgrounded ruby script never closing, and thus spawning a new ruby process for each time you loaded up the window. There were other user-experience annoyances, too. The good news is that that&#8217;s all in the past now.</p>
<p>The new version of the Wycats bundle has the following changes:</p>
<ul>
<li>I am now using a utility window, so the search window always remains in front</li>
<li>I am now tracking open windows, so if you have an open window, another one doesn&#8217;t open</li>
<li>I track all available exit conditions and correctly exit the event loop</li>
<li>Other bugfixes and tweaks</li>
</ul>
<p><a href="/wp-content/uploads/Wycats.tmbundle.zip">Download version 1.0.1 today!</a></p>
]]></content:encoded>
			<wfw:commentRss>http://yehudakatz.com/2007/11/15/textmate-search-in-a-folder-with-grep-part-deux/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
