Building Robust jQuery Plugins

  • Upload
    dympna

  • View
    45

  • Download
    0

Embed Size (px)

DESCRIPTION

Building Robust jQuery Plugins. Why bother?. $.each({ focus: 'focusin', blur: 'focusout' }, function( original, fix ){ $.event.special[fix] = { setup:function() { if ( $.browser.msie ) return false; this.addEventListener( original, $.event.special[fix].handler, true ); - PowerPoint PPT Presentation

Citation preview

  • Building Robust jQuery Plugins

  • Why bother?

  • $.each({focus: 'focusin',blur: 'focusout'}, function( original, fix ){$.event.special[fix] = {setup:function() {if ( $.browser.msie ) return false;this.addEventListener( original, $.event.special[fix].handler, true );},teardown:function() {if ( $.browser.msie ) return false;this.removeEventListener( original,$.event.special[fix].handler, true );},handler: function(e) {arguments[0] = $.event.fix(e);arguments[0].type = fix;return $.event.handle.apply(this, arguments);}};});

  • Don't reinvent the wheel

  • Given enough eyeballs, all bugs are shallow -- Eric S. Raymond

  • Infrastructure

  • Design

  • Consider the audience

  • jQuery.validator.addMethod("domain", function(value) {return /^http://mycorporatedomain.com/.test(value); }, "Please specify the correct domain");

    jQuery.validator.addMethod("nowhite", function(value) {return /^\S+$/.test(value);}, "No white space please");

    jQuery.validator.addMethod("nowhite", function(value) {return /^\d+$/.test(value);}, "Please enter only digits");

  • Test-driven development

  • Tests first

  • Behaviour-driven developement

  • Why bother?

  • QUnit

  • test("my ajax code", function() {expect(1);stop();$.get("myurl", function(response) {equals(response, "expected response");start();});});

  • Implementing

  • (function($) { $.fn.plugin = function() { return this.each(function() { // your code here }); };})(jQuery);

  • (function($) { $.fn.delegate = function(type, delegate, handler) { return this.bind(type, function(event) { var target = $(event.target); if (target.is(delegate)) { return handler.apply(target, arguments); } }); }; })(jQuery);

  • (function($) { $.fn.plugin = function(settings) { settings = $.extend({}, $.plugin.defaults, settings); return this.each(function() { // your code here }); }; $.plugin = { defaults: {} };})(jQuery);

  • Document

  • /** * The number of elements currently matched. * * @example $("img").length; * @before * @result 2 * * @property * @name length * @type Number * @cat Core */

    /** * The number of elements currently matched. * * @example $("img").size(); * @before * @result 2 * * @name size * @type Number * @cat Core */size: function() {return this.length;},

    length: 0,

  • Purpose

  • Dependencies

  • Puplic API

  • Options

  • Examples

  • Browsing

  • Release

  • jquery.plugin-1.2.3.zip- jquery-plugin-1.2.3jquery.plugin.jsjquery.plugin.min.jsjquery.plugin.pack.js- demo[-docs][-test]

  • ant tooltip

  • 1.3---* Added fade option (duration in ms) for fading in/out tooltips; IE
  • Maintain

  • Avoid Blog Comments

  • Mailing list

  • General questions

  • Tracking

  • Questions?

  • Thank you

    Goal is to show to how build a robust jQuery plugin, way beyond just coding. Your take-away should be an understanding of the surrounding infrastructure, both to be able to use and to help improve it.

    We've got about 50 minutes. My talk should take about 30 minutes, giving us 20 minutes for open discussion.

    Please ask questions whenever you want to, though I may defer some answers to the end.

    To start, lets look at the most important question.*Why bother writing jQuery plugins when jQuery makes it so easy to create things on the fly?Because jQuery helps with the basic building blocks, but isnt a magic tool to do all your work. Everything slightly more complex is worth reusing, and plugins are a proven pattern to achieve that.

    For example, you don't want to write this more than once*right? Okay, maybe you like writing stuff like that, but maybe you don't want to write that at all.

    In other words*So, reusing code is obviously a good thing, but why share it with anyone outside of your company? The incentives are those of most open-source software*With enough people using your plugin, bugs will get spotted much faster. Often enough others contribute tests and fixes for those bugs. Features requested by others may later be useful for yourself, too.

    The good thing about releasing and maintaining a jQuery plugin, when compared to other open-source software, is the low cost. The existing*lets you focus on writing code, documentation and demos, instead of setting up servers and mailing lists.

    But before we get to those topics, lets first think about*Consider writing a form validation plugin. You design it to validate forms with up to two dozens of inputs. After releasing it, people complain about bad performance, something you didnt had to bother with so far. It turns out their forms contain hundreds of inputs.In other words: You have to*consider the audience of a plugin and make design descision based on that. Are they happy with a *pony? Or do they need GPS, a racing seat and subwoofer, too?

    It certainly helps to *think about it upfront. Even if you have to change your vision of the plugin later, its important to have one.

    Therefore the most important aspect is the ability to add more features, without bloating the plugin into space. jQuery does that well with its dead-simple plugin architecture, which I talked about last year with the theme*The Onion. The basic idea is to layer plugins above other plugins and a common core.

    And while that idea is rather obvious, finding appropiate extension points for your own plugin can be a very difficult task. Its nonetheless important to always look for ways to open a plugin to let users add features without having to add those to the core of the plugin.

    Some extension points are obvious*a validation plugin should offer an API for adding custom validation methods.

    Other extension points have to be discovered, one way is to keep everything wide*When writing a jQuery plugin, the author has the option to keep everything public in code, but declare only those methods documented as the public API. Users can then hook their own code into parts of the plugin without having to modify the code itself.

    By contributing those modifications back to the plugin author, he can decide to make that hook an official part of the API, thefore supporting it in future versions. jQuery itself is a good example of that. Most helper and utility methods that are not part of the jQuery API are still accesible in the jQuery-namespace.

    For now, lets look at*The most common technique for test-driven development is unit-testing. Unit refers to the fact that only a small component of a program is tested. Test runners then indicate the result of running tests, where red*inidicates that some test failed, and green*that all is fine. Apart from that, the most prominent idea is to write*tests before writing the implementation. Benefits include improving the design of the code by thinking about the usage first. It also guarantees that every implementation has a test that works, which is hard to achieve when adding tests as an afterthought.

    Some people picked up the test-as-specification idea and took it a step further, resulting in* behaviour-driven development and related testing frameworks. Those frameworks enable the developer to write code that reads like a specification. Again, the interesting question should be:*Whether you are writing xUnit style tests or a specification, in both cases the major value is to have code that can run repeatedly without human interaction to assert the quality of the tested code. Once a program is non-trivial, any change to code that isnt covered by a test gets increasingly painful. So in the end, the point of writing tests are the tests themselve, no matter if they serve as a specification or not.

    jQuerys own unit testing framework is*QUnit. Someone familiar with xUnit testing should have no trouble using it, even though the API is not a strict port.

    The outstanding QUnit feature is support to test asynchronous code (JsUnit doesnt have that). This allows you to test ajax-code without having to implement your own sychronization scheme.

    A typical asynchronous test looks like this:*The test function accepts a string to describe the test and a function that implements the test. The call to expect sets the number of expected assertions.

    That helps to identify bugs where callbacks are never called, therefore the contained assertions never run.

    The call to stop blocks the queue of tests to execute, which is continued after running the ajax callback and the equals-assertion with start.

    Now, with basic ideas of design and testing, lets get to actually*implementing a plugin.

    Most jQuery plugins consist of this basic building blocks:*The surrounding block provides a private scope for variables declared inside it. It also declares its own $ variable to reference jQuery. This allows a user of the plugin to load jQuery, the plugin, and afterwards a different jQuery version with other plugins that depend on it. This works as long as code inside the private scope use only the local $ variable, and not jQuery directly.

    The plugin itself is exposed by adding a function to $.fn, which is an alias for jQuery.prototype. In other words, a new method is added to every jQuery instance.

    The last piece is the return of a call to this.each. That iterates over all selected elements, which can be none, and calls the function for each element. The actual plugin code is then implemented inside that function.

    As an example, lets look at another part of the delegate plugin:*

    In this case we have three distinct arguments. Most plugins need much more than that, which lead to an extension to the basic plugin pattern called options.*Here the plugin function has a single argument, settings. That object is merged with an empty object and plugin defaults, declared as $.pluginName.defaults. This provides the user with a familiar API, where all settings have sensible defaults, but can be used individually to customize the plugins behaviour. By exposing the defaults as $.plugin.defaults, a user can configure defaults once on a page, which is useful when using the same plugin several times.

    There are enough plugins that use exactly this pattern, but reducing them to make them fit on a slide would us get back to where we are now, so I just skip that.

    Instead, lets look at how to*document plugins.

    jQuery is the most prominent example of a software library where examples serve as an integral part of the documentation. Initiallly these examples, together with descriptions, were embedded in*the source code. This proved to be very ineffective, as countless bug reports just covered documentation typos. Moving this documentation to a wiki not only enabled anyone to help improve the documentation, starting with fixing typos, but also enabled running and sometimes interactive examples. The mediawiki templates used to accomplish that can be used by anyone to document their plugin.

    No matter if you plan to use the official jQuery wiki or your blog, the following elements should be documented in any case:*Why should somebody use it?*What files are necessary to use the plugin?*What methods are available? Whats their purpose and signature?*What options are available, and what are their defaults?*A running example for the main purpose of the plugin is the absolute minimum. Each method should also have a running example, options should have at least example code.

    The wiki is not the optimal solution to *browse the documentation: It isnt available offline and cant be bundled with a download. To counter that, tools are available that export content from the wiki to a xml-based format, and from their to various*other formats, like Yehuda's Visual jQuery, here in Remy Sharps updated version.

    There's still some work to do to make this easily available for plugin authors (even after a year of first saying that).

    In the meantime, lets look at how to Release*Producing stable releases is essential to get a plugin used. While early adopters may checkout the latest revision from the code repository, most users wont bother with that. A release should contain *the source files, as well as minified and packed versions. A zip archive (dont bother people with tar.gz and the like, thats extremely annoying on Windows systems, the size doesnt matter anyway) should contain a folder with the same name as the zip file, so a user can just unpack the content somewhere, without having to first create a folder.

    Demos should be under demo, docs and test, if included, accordingly.

    To automate creation*an ant-based build is avaible in jQuerys subversion repository. It makes the creating minified, packed and zip files trivial.

    Also part of a release should be a*changelog. This is a simple list of changes like bug fixes and new features with a very short description, and often the ID of the issue in the bug tracking system for more details. This is essential when people are checking if a certain bug is fixed in the new release, or a feature implemented. Keeping that changelog up-to-date should done whenever commiting a change thats much easier than updating it before a release.

    You can upload a release to*plugins.jquery.com. You'll provide a version, the zip file and release notes. But before uploading, you need to create a*project page, which contains links the plugin homepage, documentation and code repository. It also features a issue tracker, which can be used for*maintenance. Though the first issue to tackle is to answer support questions. In general, I recommend to*When Ive initially released jQuery plugins by creating pages on my Wordpress blog, I answered questions in the comments. The pages still exist, but I had to close the comment sections. One of the pages had 572 comments, with an average of 10 comments per day. Due to the format, quite a few messages got repeated , while no one but myself answered them.

    To give you a better idea, here are the first 20 of those 572 comments.*And it goes on like this for hundreds of pages.

    Thats why I eventually moved all support to the jQuery *mailing list, asking people to put the plugin-name in square brackets into the subject. The archive is searchable and available in different formats, most notably the*Google Groups web interface and*Nabble.

    A lot of questions arent even plugin-specific, but rather*general JavaScript problems, like the infamous trailing-comma-problem. Giving others an easy way to answer those questions helps focusing on the specific problems.

    We are now moving away Google Groups, as you should notice in the next weeks and months. Stack Overflow can help, especially for general problems.

    At some point questions require further action, thats where*tracking comes into play.

    You have to deal with bug reports, feature requests and contributions. A bug tracker is invaluable to handle that. The plugin repository provides one, an alternative would be Google Code, which makes sense when using the subversion repository already.

    Most people dont have a problem with creating tickets and adding a bit of description, so its always a good idea to just ask them to create it, saving the time for actually working on issues.

    And that was the last chapter, so*Yes? No? Anyway*Thank you for your attention!*