Raindrop/Extensions

From MozillaWiki
Jump to: navigation, search
Draft-template-image.png THIS PAGE IS A WORKING DRAFT Pencil-emoji U270F-gray.png
The page may be difficult to navigate, and some information on its subject might be incomplete and/or evolving rapidly.
If you have any questions or ideas, please add them as a new topic on the discussion page.

There are two basic types of extensions: Front End and Back End extensions.

Front End extensions normally modify the display of the UI, and Back End extensions normally add or modify the back end data.

Data Miners are a specific type of Back End extension.

Front End Extensions

Please read the Front End notes to get some backround on the context in which Front End extensions run.

There are some Front End extensions in the raindrop/client/lib/rdw/ext directory. These may be moved out at some point to a raindrop/client/ext directory. You can look at those extensions to get a feel for how they are constructed. the MailingList one is interesting because it extends a few widgets.

It is likely that most extensions will want to extend an existing module, so the extension model is focused on attaching methods and properties to existing modules. However, it is possible to create an extension that just load a new, independent component. In that case, rd.applyExtension() will not be called.

This may change over time, but as of right now, the basic construction of an extension is shown below, called "sample". An extension can register certain types of extension points, all listed below: "before", "after", "around", "replace", "add" and "addToPrototype". Typically an extension will only use one or two types of extension points.

//Tell the loader that rdw/Message will be modified,
//and give the name of this extension. Also indicate
//the dependencies for the extension.

run.modify("rdw/Message", "ext/sample",
["rd", "rdw/Message"], function (rd, Message) {

    //Tell raindrop you want to apply an extension, and what will be extended.
    rd.applyExtension("ext/sample", "rdw/Message", {

        //Register "before" extension points.
        //These extension functions should be called before
        //the matching function on rdw/Message
        before: {
            //This postCreate will be called before an instance of
            //rdw/Message's postCreate method is called.
            postCreate: function() {
            }
        },

        //Register "after" extension points.
        //These extension functions should be called before
        //the matching function on rdw/Message
        after: {
            //This postCreate will be called after an instance of
            //rdw/Message's postCreate method is called.
            postCreate: function() {
            }
        },
        
        //Register "around" extension points.
        //These extension functions should be called before
        //the matching function on rdw/Message, and this extension 
        //point has the option to to call the original rdw/Message 
        //function or bypass it completely. It also has the ability
        //to modify the return value from the rdw/Message function call.
        //This type of extension point is very experimental and may 
        //change due to its use of arguments.callee
        around: {
            //This postCreate will be called before an instance of
            //rdw/Message's postCreate method is called. If it wants to
            //call rdw/Message's postCreate function, it can 
            postCreate: function() {
                //Demonstrates calling the rdw/Message function 
                //that this extension point is wrapping around.
                var value = arguments.callee.target.apply(this, arguments);
                //Modify value then return it.
            }
        
        },
    
        //Register "replace" extension points.
        //These extension functions replace the the matching function
        //on rdw/Message. It will first check rdw/Message for 
        //the matching function, then look at rdw/Message's prototype
        //for the method.
        replace: {
            //This postCreate will be replace rdw/Message's
            //prototype.postCreate
            postCreate: function() {
            }
        },
    
        //Register "add" extension points.
        //These extension functions will be added to rdw/Message.
        //Note that they are added to rdw/Message, and *not*
        //rdw/Message's prototype, so they will not be callable
        //from instances of rdw/Message.
        add: {
            //This function will be placed on rdw/Message,
            //it is like adding a "class-level"
            //function vs an instance-level function.
            getInstances: function() {
            }
        },
    
        //Register "addToPrototype" extension points.
        //These extension functions will be
        //added to rdw/Message's prototype, and *not* to rdw/Message.
        addToPrototype: {
            //This function will be placed on 
            //rdw/Message's prototype, it is like adding an
            //instance-level function vs a "class-level" function.
            onActionClick: function() {
            }
        }
    });
});


rd.applyExtension() take the name of the extension so it can dynamically enable or disable it on the fly. For example, if the user decides to disable it via the Extender web app. rd.applyExtension() actually creates another function wrapper that checks the extension registry for each call, to make sure the extension is enabled. If it is, then the extension function is called. If not, then it is just skipped.

Extensions are only loaded when the module they are extending has been loaded.

Still to do

Right now, if the extender web app is used, it only saves extensions in CouchDB, so if the CouchDB database is deleted, then the extension is lost. We need a nice way to export an extension to disk so it can be put into source control. Perhaps this will fall out naturally once we work out extension distribution.

Extensions can add CSS to the page by using rd.addStyle(). It uses the logical paths names used for module names, for example, rd.addStyle("ext/foo/main") will load ext/foo/main.css in a link tag. Ideally this CSS could be inlined via a distribution tool, but there are path implications for url() references in the CSS.

Currently the metadata on an extension is pretty light: no author or version information is stored. We also need to allow for registering extensions so that they only apply to specific web apps vs. an extension that applies for all web apps.

Distribution of extensions still needs to be defined. Something safe and secure would be nice.

How to get involved with Extensions: Post a message or search the archives on the mailing list with "Extensions:" in the Subject.