<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel><atom:link rel="hub" href="http://tumblr.superfeedr.com/" xmlns:atom="http://www.w3.org/2005/Atom"/><description>Winton Welsh has a blog</description><title>wintoni.us</title><generator>Tumblr (3.0; @wintonius)</generator><link>http://wintoni.us/</link><item><title>The Pug King</title><description>&lt;img src="http://28.media.tumblr.com/eydwi1WKJq2kzlpqq6hsh10Io1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;The Pug King&lt;/p&gt;</description><link>http://wintoni.us/post/144408203</link><guid>http://wintoni.us/post/144408203</guid><pubDate>Sat, 18 Jul 2009 17:35:26 -0700</pubDate></item><item><title>jQuery Plugin Patterns</title><description>&lt;pre class="sh_javascript_dom"&gt;// Simplest plugin ever

(function($) {
  $.myPlugin = function() {};
})(jQuery);


// Two ways to call it

jQuery.myPlugin();

jQuery(function($) {
  $.myPlugin();
});
&lt;/pre&gt;
&lt;p&gt;With the realization that jQuery gives you no more than this basic structure to write fat, complex plugins, the youthful sense of exploration that brought you to jQuery quickly evaporates into an empty void of nothingness. You google “jQuery plugin tutorial” and ask yourself, “How is this better than &lt;insert framework here&gt;’s class helpers?” Your head feels strangely pressurized. “And how would I test…” Your brain explodes, Scanners-style, without even an understanding of the question you are about to ask. The very moment your brain begins to disentigrate, you realize that you never were alive to begin with; your existence was simply a predetermined, predictable set of events. The question of God is, and always was, irrelevant. The revelation itself is unimportant and unnoticed. You cease to exist.&lt;/p&gt;
&lt;p&gt;OK, so that is the worst case scenario, albeit surprisingly frequent and plausable :). I am going to show you that using pure javascript to write your plugins is not only easier to read and comprehend, but extremely powerful. And then I’m going to show you how to test your plugins using &lt;a href="http://docs.jquery.com/QUnit"&gt;qUnit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here is quick guide to the “class” structure I will discuss, for you programmer types:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;(function($) {

  // Private class variable
  var a;

  // Private class method
  function doSomething() {}

  // Public class method
  $.myPlugin = function() {

    // Private instance variable
    var b;

    doSomething();
    doSomethingElse();

    // Private instance method
    function doSomethingElse() {}
  };
})(jQuery);
&lt;/pre&gt;
&lt;p&gt;Before writing a real world plugin, let’s define a strategy for calling jQuery plugins.&lt;/p&gt;
&lt;p&gt;jQuery plugins are usually accessed through a single public method. Often times, with more complex plugins, you will want to specify an action to take (play, pause, or stop, for example). You may also want to pass in any number of options to modify the plugin’s functionality or to specify event callbacks.&lt;/p&gt;
&lt;p&gt;Given the requirements, this seems like a pretty versatile way to call a plugin:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;$.myPlugin({ option: false })
// or
$.myPlugin('action', { option: false });
&lt;/pre&gt;
&lt;p&gt;The implementation:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;(function($) {
  var defaults = { option: true };
  var options;

  $.myPlugin = function(action, new_options) {
    if (typeof(action) == 'object') {
      new_options = action;
      action = 'initialize';
    }
    setOptions(new_options);
    // Do something
    // if (action == 'initialize')
    return options;
  };

  function setOptions(new_options) {
    options = $.extend({}, defaults, options, new_options);
  }
})(jQuery);
&lt;/pre&gt;
&lt;p&gt;The options variable maintains state across multiple myPlugin calls. If myPlugin is called without options, the options variable will equal the defaults variable. If options are specified, they are merged with the default options. If myPlugin is called once again with options, those options will be merged with the previous options, and so on.&lt;/p&gt;
&lt;p&gt;Now that you know how to handle basic options, let’s specify an event callback as well:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;$.myPlugin({
  option: false,
  event: function() {}
})
// or
$.myPlugin('action', {
  option: false,
  event: function() {}
});
&lt;/pre&gt;
&lt;p&gt;The implementation:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;(function($) {
  var defaults = { option: true };
  var events = $({});
  var options;

  $.myPlugin = function(action, new_options) {
    if (typeof(action) == 'object') {
      new_options = action;
      action = 'initialize';
    }
    setOptions(new_options);
    // Do something
    // if (action == 'initialize')
    //   events.trigger('event');
    return options;
  };

  function setOptions(new_options) {
    $.each(new_options, function(event, fn) {
      if (typeof(fn) == 'function') {
        events.unbind(event);
        events.bind(event, fn);
      }
    });
    options = $.extend({}, defaults, options, new_options);
  }
})(jQuery);
&lt;/pre&gt;
&lt;p&gt;The setOptions method detects when an option is a function. If so, it binds that event to an empty jQuery object.&lt;/p&gt;
&lt;p&gt;Let’s harness the real power of jQuery by allowing access to the plugin via element collections:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;// Simplest element-based plugin ever

(function($) {
  $.fn.myPlugin = function() {
    return this;
  };
})(jQuery);

// Two ways to call it

jQuery('#id').myPlugin();

jQuery(function($) {
  $('#id').myPlugin();
});
&lt;/pre&gt;
&lt;p&gt;The jQuery object has a special fn object that you can assign functions to. Any function attached to fn will also be available to jQuery element collections.&lt;/p&gt;
&lt;p&gt;The new plugin call:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;$('#id').myPlugin({ option: false });
// or
$('#id').myPlugin('action', { option: false });
&lt;/pre&gt;
&lt;p&gt;Handling plugin options is a bit different when you involve element collections. Your plugin acts on each individual element, and therefore must maintain state for each element separately.&lt;/p&gt;
&lt;p&gt;The implementation:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;(function($) {
  var defaults = { option: true };

  $.fn.myPlugin = function(action, options) {
    if (typeof(action) == 'object') {
      options = action;
      action = 'initialize';
    }
    
    this.each(function(el) {
      el = $(this);
      setOptions(el);
      // Do something
      // if (action == 'initialize')
    });

    function setOptions(el) {
      options = $.extend({}, defaults, el.data('my_plugin:options'), options);
      el.data('my_plugin:options', options);
    };

    return this;
  };
})(jQuery);
&lt;/pre&gt;
&lt;p&gt;Here we use jQuery’s data store to associate options with each individual element.&lt;/p&gt;
&lt;p&gt;A plugin call with an event callback:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;$('#id').myPlugin({
  option: false,
  event: function() {}
});
// or
$('#id').myPlugin('action', {
  option: false,
  event: function() {}
});
&lt;/pre&gt;
&lt;p&gt;The implementation:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;(function($) {
  var defaults = { option: true };

  $.fn.myPlugin = function(action, options) {
    if (typeof(action) == 'object') {
      new_options = action;
      action = 'initialize';
    }
    
    this.each(function(el) {
      el = $(this);
      el.trigger('event');
      setOptions(el);
      // Do something
      // if (action == 'initialize')
    });

    function setOptions(el) {
      $.each(options, function(event, fn) {
        if (typeof(fn) == 'function') {
          el.unbind(event);
          el.bind(event, fn);
        }
      });
      options = $.extend({}, defaults, el.data('my_plugin:options'), options);
      el.data('my_plugin:options', options);
    };

    return this;
  };
})(jQuery);
&lt;/pre&gt;
&lt;p&gt;As with options, events are attached to the element instead of maintaining state through a class variable.&lt;/p&gt;
&lt;p&gt;Finally, let’s take a look at testing this plugin structure using qUnit. The implementation:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;(function($) {
  var defaults = { option: false };

  $.fn.myPlugin = function(action, options) {
    if (typeof(action) == 'object') {
      options = action;
      action = 'initialize';
    }
    
    this.each(function(el) {
      el = $(this);
      setOptions(el);
      // Do something
      // if (action == 'initialize')
      el.trigger('test_each', {
        functions: { setOptions: setOptions },
        variables: { defaults: defaults, options: options }
      });
    });

    function setOptions(el) {
      options = $.extend({}, defaults, el.data('my_plugin:options'), options);
      el.data('my_plugin:options', options);
    };

    return this;
  };
})(jQuery);
&lt;/pre&gt;
&lt;p&gt;Here we use a “test_each” event to provide context for the tests. Events allow you to test private variables and functions, eliminating the need for thinking about accessibility all the time. It also eliminates the need for having “this” everywhere and generally making the javascript ugly.&lt;/p&gt;
&lt;p&gt;Here is how we might test the last example:&lt;/p&gt;
&lt;pre class="sh_javascript_dom"&gt;jQuery(function($) {
  var functions, variables;

  function setup() {
    var accessor = $('#id').myPlugin({
      option: true,
      test_each: function(event, accessor) {
        functions = accessor.functions;
        variables = accessor.variables;
      }
    });
  }

  module('setOptions', { setup: setup });
  test('should extend the default options if options specified', function() {
    equals(variables.options.option, true);
  });
  test('should assign default options if no options specified', function() {
    $('#id').data('my_plugin:options', null).myPlugin();
    equals($('#id').data('my_plugin:options'), variables.defaults);
  });
  test('should store options in the data store', function() {
    equals($('#id').data('my_plugin:options'), variables.options);
  });
});
&lt;/pre&gt;
&lt;p&gt;And finally, the HTML to execute the test:&lt;/p&gt;
&lt;pre class="sh_html"&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;link href="jquery.testsuite" media="print" rel="Stylesheet" type="text/css" /&gt;
  &lt;script type="text/javascript" src="jquery-1.3.2"&gt;&lt;/script&gt;
  &lt;script type="text/javascript" src="jquery.testrunner"&gt;&lt;/script&gt;
  &lt;script type="text/javascript" src="jquery.my_plugin"&gt;&lt;/script&gt;
  &lt;script type="text/javascript" src="jquery.my_plugin.qunit"&gt;&lt;/script&gt;
  &lt;title&gt;jquery.my_plugin&lt;/title&gt;
&lt;/head&gt;

&lt;body&gt;
  &lt;h1&gt;jquery.my_plugin&lt;/h1&gt;
  &lt;h2 id="banner"&gt;&lt;/h2&gt;
  &lt;h2 id="userAgent"&gt;&lt;/h2&gt;
  &lt;ol id="tests"&gt;&lt;/ol&gt;
  &lt;div id="main"&gt;
    &lt;div id="id"&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
&lt;/pre&gt;
&lt;p&gt;Learn more about qUnit &lt;a href="http://docs.jquery.com/QUnit"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Personally, this way of doing things has reinvigorated my curiosity towards jQuery. Most of all, it has dispelled my worries about jQuery only being a tool for designers. &lt;a href="mailto:mail@wintoni.us"&gt;Let me know&lt;/a&gt; if you guys find an even cleaner way to execute and test complex objects.&lt;/p&gt;</description><link>http://wintoni.us/post/123029056</link><guid>http://wintoni.us/post/123029056</guid><pubDate>Sat, 13 Jun 2009 12:50:00 -0700</pubDate><category>jquery</category><category>javascript</category></item><item><title>Git Externals</title><description>&lt;p&gt;We were having trouble figuring out the best way to handle our application’s external git dependencies at &lt;a href="http://bleacherreport.com"&gt;Bleacher Report&lt;/a&gt;. We wanted to be able to “freeze” externals to simplify project setup and deployment. We also wanted our developers to be able to quickly unfreeze externals, work on them, and freeze them back.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html"&gt;Submodules&lt;/a&gt; and &lt;a href="http://github.com/37signals/cached_externals"&gt;cached_externals&lt;/a&gt; did not fit those requirements. I really like &lt;a href="http://github.com/pat-maddox/giternal"&gt;giternal&lt;/a&gt;, which works well (and has a way cool name), but I don’t like how it shares a single gzipped &lt;code&gt;.git&lt;/code&gt; snapshot across all users on a repository. I also think it could be made simpler in some areas. Many thanks to &lt;a href="http://www.patmaddox.com/"&gt;Pat Maddox&lt;/a&gt; for getting the ball rolling.&lt;/p&gt;
&lt;p&gt;Introducing &lt;a href="http://github.com/winton/externals"&gt;externals&lt;/a&gt;. Let’s install the gem.&lt;/p&gt;
&lt;p&gt;Terminal:&lt;/p&gt;
&lt;pre class="sh_sh"&gt;gem sources -a &lt;a href="http://gems.github.com"&gt;http://gems.github.com&lt;/a&gt;
sudo gem install winton-externals
&lt;/pre&gt;
&lt;p&gt;Create a configuration file.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;my_repo/config/externals.yml&lt;/code&gt; &lt;b&gt;or&lt;/b&gt;&lt;br/&gt;&lt;code&gt;my_repo/.externals.yml:&lt;/code&gt;&lt;/p&gt;
&lt;pre class="sh_xml"&gt;rails:
  repo: git://github.com/rails/rails.git
  path: vendor
&lt;/pre&gt;
&lt;p&gt;Freeze Rails into your repository.&lt;/p&gt;
&lt;pre class="sh_sh"&gt;cd my_repo
externals freeze
&lt;/pre&gt;
&lt;p&gt;You should see a frozen copy of Rails in your vendor directory. Now you can commit to your parent repository like normal.&lt;/p&gt;
&lt;pre class="sh_sh"&gt;git add .
git commit -m "Freezing Rails"
git push origin master
&lt;/pre&gt;
&lt;p&gt;When you unfreeze an external, it becomes its own repository again. Let’s pull the latest changes from the Rails repository.&lt;/p&gt;
&lt;pre class="sh_sh"&gt;externals unfreeze
cd vendor/rails
git pull origin master
cd ../../
externals freeze
&lt;/pre&gt;
&lt;p&gt;You can also freeze and unfreeze specific externals using a regular expression match.&lt;/p&gt;
&lt;p&gt;This would also freeze rails:&lt;/p&gt;
&lt;pre class="sh_sh"&gt;externals freeze ra
&lt;/pre&gt;
&lt;p&gt;In case you can’t remember the freeze state of your externals:&lt;/p&gt;
&lt;pre class="sh_sh"&gt;externals status
&lt;/pre&gt;
&lt;p&gt;And last but not least, some nifty shortcuts:&lt;/p&gt;
&lt;pre class="sh_sh"&gt;externals st # status
externals fr # freeze
externals un # unfreeze
&lt;/pre&gt;
&lt;p&gt;An additional thanks to &lt;a href="http://github.com/tongueroo"&gt;Tung Nguyen&lt;/a&gt; for helping me get this to 1.0.0 and &lt;a href="http://github.com/ovoice"&gt;ovoice&lt;/a&gt; for updating the &lt;a href="http://wiki.github.com/winton/externals"&gt;wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;More details on the &lt;a href="http://github.com/winton/externals"&gt;externals GitHub page&lt;/a&gt;.&lt;/p&gt;</description><link>http://wintoni.us/post/100956835</link><guid>http://wintoni.us/post/100956835</guid><pubDate>Mon, 27 Apr 2009 22:38:00 -0700</pubDate><category>externals</category><category>ruby</category><category>git</category></item><item><title>Kicking myself for not bringing up some terminals to further...</title><description>&lt;img src="http://27.media.tumblr.com/eydwi1WKJmf5axs5AHd8OKTmo1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Kicking myself for not bringing up some terminals to further exemplify my 1337ness.&lt;/p&gt;</description><link>http://wintoni.us/post/97361248</link><guid>http://wintoni.us/post/97361248</guid><pubDate>Fri, 17 Apr 2009 17:58:00 -0700</pubDate><category>1337</category><category>workstation</category></item><item><title>Its usually pretty easy to tell what Henry is thinking about.</title><description>&lt;img src="http://27.media.tumblr.com/eydwi1WKJm6kh7kfNQYDPgMQo1_r2_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Its usually pretty easy to tell what Henry is thinking about.&lt;/p&gt;</description><link>http://wintoni.us/post/95300620</link><guid>http://wintoni.us/post/95300620</guid><pubDate>Sat, 11 Apr 2009 17:53:00 -0700</pubDate><category>Henry</category><category>bacon</category></item><item><title>Today in Golden Gate Park I encountered a wild Henry. I was...</title><description>&lt;img src="http://25.media.tumblr.com/eydwi1WKJlcdp5jivjYGIsB5o1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Today in Golden Gate Park I encountered a wild Henry. I was attacked and mauled.&lt;/p&gt;</description><link>http://wintoni.us/post/88566833</link><guid>http://wintoni.us/post/88566833</guid><pubDate>Sat, 21 Mar 2009 14:50:00 -0700</pubDate></item><item><title>RubyMachine</title><description>&lt;p&gt;This is the first in my “rambling about ideas I don’t have the time to create, but want to take credit for if someone makes it” series. My first entry is about an idea I am calling RubyMachine, a Ruby platform for novices to create and deploy web applications.&lt;/p&gt;
&lt;p&gt;Rails is not easy. Yes, to people who do this stuff for a living, Rails is easy in the sense that everything before it sucked. But its not PHP easy, not “takes your nephew an hour to learn” easy. I want a Ruby web framework with a directory structure a child can understand:&lt;/p&gt;
&lt;pre class="sh_sh"&gt;index.haml
index.rb
about/
  me.erb
  me.rb
  _interests.markdown
&lt;/pre&gt;
&lt;p&gt;Every file pairing consists of a markup file and an optional Ruby companion script. The companion script contains non-classed Ruby code with access to a query string hash and a database via &lt;a href="http://datamapper.org"&gt;DataMapper&lt;/a&gt;. Routes resemble the file paths (without extensions). Partials are underscored and cannot be accessed externally.&lt;/p&gt;
&lt;p&gt;Locally, install the gem, run &lt;b&gt;rubymachine&lt;/b&gt; in your project’s home directory, and your development server starts. When you’re ready to publish, visit &lt;b&gt;rubymachine.com&lt;/b&gt; and deploy your project straight from a &lt;a href="http://github.com"&gt;GitHub&lt;/a&gt; repository to your own dedicated server for a low monthly price (kaching).&lt;/p&gt;
&lt;p&gt;I realize that I’m skipping a few details like layouts, plugins, and rendering partials. However, with these basic guidelines, I think this could be a cool way to get Ruby and web development out to a broader audience.&lt;/p&gt;</description><link>http://wintoni.us/post/88427777</link><guid>http://wintoni.us/post/88427777</guid><pubDate>Sat, 21 Mar 2009 01:13:00 -0700</pubDate><category>ideas</category></item><item><title>Somebody watching me pretend to be someone I once saw getting...</title><description>&lt;img src="http://28.media.tumblr.com/eydwi1WKJla0dkjqrpFEUZuho1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Somebody watching me pretend to be someone I once saw getting their picture taken with hipster glasses on. If the shoe.&lt;/p&gt;</description><link>http://wintoni.us/post/88114444</link><guid>http://wintoni.us/post/88114444</guid><pubDate>Thu, 19 Mar 2009 23:02:00 -0700</pubDate><category>photo</category></item><item><title>Ubistrano on Rails Envy</title><description>&lt;p&gt;I am thankful to have received a brief plug last Friday from Jason Seifer on &lt;a title="Rails Envy Podcast #70" href="http://www.railsenvy.com/2009/3/14/rails-envy-podcast-episode-070-03-13-2009"&gt;Rails Envy Podcast #70&lt;/a&gt;. Hosting along side Obie Fernandez, he briefly mentioned my server provisioning tool, &lt;a title="Ubistrano" href="http://github.com/winton/ubistrano"&gt;Ubistrano&lt;/a&gt;, and got me a few more watchers on &lt;a href="http://github.com/winton"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ubistrano came about primarily because I did not want to learn &lt;a title="Puppet" href="http://github.com/lak/puppet"&gt;Puppet&lt;/a&gt;’s (ugly) proprietary scripting language. &lt;a title="Sprinkle" href="http://github.com/crafterm/sprinkle"&gt;Sprinkle&lt;/a&gt; has great promise, but at the time was short on examples and had no stable versioning in place. It made the most sense to throw together some helper methods and write the provisioning process myself. I think it is still the quickest way to get rolling without having to cobble together recipes from random places. However, you are restricted to my very specific server stack.&lt;/p&gt;
&lt;p&gt;I am writing this to outline my view of Ubistrano 2.0, which will probably be released under a different name. Firstly, I want to eliminate &lt;a href="http://github.com/jamis/capistrano" title="Capistrano"&gt;Capistrano&lt;/a&gt; altogether. I want everything to be run from a binary on the target machine (i.e. ubistrano apache:install). Using this binary you can run automated tasks written in a nice Ruby DSL. The DSL handles things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Building and installing binaries &lt;/li&gt;
&lt;li&gt;Piping STDIN/STDOUT to and from other processes &lt;/li&gt;
&lt;li&gt;Adding and removing strings from files &lt;/li&gt;
&lt;li&gt;Creating a cohesive install process (return where you left off) &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;…and anything else you need to provision a system. The solution I am describing becomes more of a generic automation tool, one that you could also use to install the latest version of Git, manage a project’s externals, upload SSH keys, and other local tasks. Ideally these tasks could be packaged as gems for quick installation to a target system.&lt;/p&gt;
&lt;p&gt;So far I’m looking into &lt;a title="Ropen" href="http://github.com/codahale/ropen"&gt;Ropen&lt;/a&gt; and &lt;a title="HighLine" href="http://github.com/JEG2/highline"&gt;HighLine&lt;/a&gt; to do the heavy lifting for me. Other than the DSL and task execution process, I want to keep this thing light. Suggestions welcomed.&lt;/p&gt;</description><link>http://wintoni.us/post/86885559</link><guid>http://wintoni.us/post/86885559</guid><pubDate>Mon, 16 Mar 2009 00:56:00 -0700</pubDate><category>ubistrano</category></item></channel></rss>
