Artificer blogged

NAMFox Re-Architecture Deep Dive #1: Introduction

So now that I have some spare time to work on NAMFox again (and what sweet spare time it is...), I thought about what I actually want to do with it in the next version. There are a lot of suggestions and bugs out there for me to take care of, so there are definitely things to do. However, as of late, I have been increasingly dissatisfied with the internal architecture of NAMFox.

Most (useful) pieces of software are built from many different components working together to meet some need. If you think about it this way, then it's not a giant leap to compare software to cars. There are a lot of different parts that go into them, and none of those parts on their own meet the same need that the car as a whole does. Both can upgrade their parts to provide more value for the consumer, and both can lose value just by aging and hence, not staying up-to-date.

One circumstance that drifts away from the analogy is when software upgrades, it may still provide additional value to consumers, but it can start to degenerate internally. As it becomes easier and easier for developers to "hack" new features rather than building them correctly, then the development cycle enters this spiral of horror until no one wants to touch it again. There are many horror stories involving real software that met this fate on The Daily WTF. It's funny when you're sitting on the outside, but you can get pretty desperate when you're on the inside.

Fortunately, NAMFox is nowhere near that state, but I think now is a good time to re-architect it so that its parts are working more cohesively than ever before. My end goals are to (1) make maintainability easier and (2) provide a better environment for other people to make their own tweaks to the tool. This also means faster turnaround time for bug fixes and suggestions. Back in early 2007, I did much of the same thing with the code that Oz and I had been working on, and after learning yet another slew of new things, I'm ready to tackle the challenge again.

So how do I achieve this? There are a couple of specific means I have in mind.
  1. Build software on the Firefox platform in the way it was meant to be built.

    When I started working on this tool, I was familiar with JavaScript in the browser, not JavaScript in a Firefox extension. As a result, most of the new code I added may have been great ideas for web pages, but not the best for extensions. Over time as I learned more about the extension model, I improved, but when there is already of lot of infrastructure built (especially one that works), one becomes wary of making large changes that could rock the boat.

  2. Disentangle the components' dependencies on each other.

    A major problem that leads to software rot is when many parts depend on each other to survive—that is, they cannot be separated from the infrastructure in which they are cemented. I feel that this is the case with too many of the current NAMFox components, especially in the areas around the user interface—markup buttons, drop downs, or simply just managing the HTML that NAMFox puts on the page. I think it's fine for dependencies among components to exist, but when they become too interleaved to see what depends on what, then it's time for a change.

  3. Utilize ideas from other tools and frameworks to build better software.

    Over the past couple of months, I have seen a few other developments in the software industry that have given me great ideas for how to make NAMFox better (internally). Specifically, researching and taking little bits and pieces from the architecture of jQuery, ASP.NET MVC, and even Firebug has yielded some interesting results.

    Just tonight someone messaged me about making a tweak in his own NAMFox, but he made a mistake in rebuilding the package; Firebug's build system uses Apache Ant, which is great because it's cross-platform and you could do a hell of a lot more stuff with it than with a batch file, which was what Oz and I were using. Although you have to download Java (collective groan) to run it, it's a phenomenal build system that probably will eventually let me automate deploying new versions to the web. (The manual process is tedious and error-prone.)

    ASP.NET MVC had a visible implementation of URL routing (similar to Apache's mod_rewrite) where requests that go to a certain URL are received by certain "handlers." Today I have a problem in that an event is raised when a page is loaded, and then I have to check about 50 different things to determine what I should do on that page. But now, I can restructure how functionality is added to pages. This means that this particular code goes away:

    code
    function onDOMContentLoaded(e) {
      var url = e.originalTarget.location.href;
      if (url.match(/forums\/\d+\/t\d+/))
      {
        pageController.addQuickThreadFunctionality(context);
      } 
      else if (url.match(/forums\/\d+/))
      {
        pageController.addQuickForumFunctionality(context);
      } 
      else if (url.match('edit_profile.html$') || fn == 'forum_settings')
      {
        //Add markup buttons to Forum Settings or guestbook
        pageController.addSignatureButtons();
      } 
      else if (url.match(/members\/guestbook/) && fn == 'sign')
      {
        pageController.addGuestbookButtons();
      }
    }
    // And so on...
    


    And I can replace it with something along these lines:

    code
    // SETUP
      var r = new RoutingHandler();
      r.addRoute(new Route("{subdomain}neoseeker.com/forums/{forumId}/t{threadId}-{*threadname}/", new ViewThreadHandler());
    
      // somewhere else...
    
      function ViewThreadHandler() {
      }
    
      ViewThreadHandler.prototype = {
          handleRequest: function(routeData) {
              // routeData has properties for all of
              // the wild cards in the route. For example:
              //  subdomain  -> "www."
              //  forumId    -> "115"
              //  threadId   -> "867089"
              //  threadName -> "sandbox"
            
              // Add Quick Reply, Quick Edit, etc.
          }
      };
    
    // Now handle requests.
    
      function onDOMContentLoaded(e) {
          r.handleRequest(e.originalTarget.location.href);
      }
    


    I can be as granular or as specific as I want with these patterns. This example showed one of the more complicated patterns, which matches the URL that appears whenever I view a thread on Neoseeker. This will make it much easier to add new functionality to other pages, because all I need to do is add a new route and handler for that URL, and BAM: we're golden.

    jQuery is a particularly quaint API for JavaScript developers that abstracts away a lot of the complexities and cross-browser inconsistencies of JavaScript that deters a lot of people from getting into JavaScript in the first place. (Look here for some tutorials.) I don't develop web sites, so I was never interested in jQuery for the longest time. What finally attracted me was its simplicity. Really, how cool is it that all you have to do to make every div on the page fade away when you click it is this:

    code
    $(function() {
        $("div").click(function() {
            $(this).fade("slow");
        });
    });
    


    Hundreds of lines of painful code reduced to fewer than 5. Awesome.

    So I thought why not bring the same sort of simplicity over to NAMFox? An example is the repetitive code you normally type to invoke an XPCOM component.1

    Before, to get the preferences service:
    code
    Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
    


    After:
    code
    $("@mozilla.org/preferences-service;1").service("nsIPrefService");
    


    So it's not as elegant as what the jQuery guys have put together, but simple time-savers like this can make working with NAMFox a much more pleasurable experience.
I apologize that this got a little long, but I am genuinely excited about making these latest upgrades to NAMFox, the right way. I'll continue to blog more about these architecture changes more for myself as a journal that I can look back on, but I would be happy to answer any questions people have.

1 XPCOM components are usually services which exist only once within the scope of a Firefox session, including across multiple windows. An example of one is the NAMFox Custom Message manager, which is the data store for custom messages; it's an XPCOM component because custom messages can't get out of sync across different windows. Otherwise, you can have some very frustrated people out there. :(

namfox software architecture technology neoseeker related

Responses (7)

0 thumbs!
#
bobbonew Dec 21, 08
Good blog post Art, I completely agree with you about coding the wrong way just to get it to work. Sometimes I just get so frustrated trying to get something to work that i'll sacrifice the backend of it, such a bitch when it comes crumbling down in your face.

Now I realize why your so good at preg_replace() and regex doh!
0 thumbs!
#
Artificer Dec 21, 08
Yeah, it's really nasty when that does happen. It's especially bad when you're the only one working on the project, because it's much more difficult to be accountable to yourself than it is to be accountable for others who have an interest in the project's success.

Ha, you should see the file where NAMFox takes HTML and translates it back to markup. -.-
0 thumbs!
#
VulcanRaven Dec 21, 08
I love it when you talk technical to me. <3
0 thumbs!
#
Chiggins Dec 21, 08
Those damn build errors.

Great blog post. It's getting me interested on learning how to work with the extensions.

It's also fun to take a look through the NAMFox source. Seeing all of those hidden things you've put in there.
0 thumbs!
#
Harvest Moon girl Dec 21, 08
I just read all of that but only managed to understand 5% and smile at the last comment you made. Haha, I never made the connection between the nickname and NAMFox, foxshy.
0 thumbs!
#
Vermillion Dec 21, 08
I'm going to learn jQuery just because of this. I never though there noticeable differences between page JS and addon JS. In fact the first time I added my sword to the drop down smile list everything looked the same...
Art, stop teaching me things .-. .


0 thumbs!
#
Artificer Dec 22, 08
Maybe I should clarify. jQuery in and of itself is a JavaScript library that was designed for web page developers to do things with JavaScript very easily. Even Neoseeker uses it in a couple of places. What I thought was very helpful looking at jQuery was that the functions and functionality it exposed—its API—were intuitive, simple, and quick to use. Consequently, I thought to take some of the ideas from jQuery (e.g. the $() function) and apply it to pain points in NAMFox, an example of which I showed in the article.

There aren't really noticeable differences between the JS you use for web pages and the JS you use for add-ons in terms of syntax. However, there are differences in the programming model you should use; you also have access to many more services in an add-on (such as file input/output, preference reading/writing, etc.).
(0.0528/d/www3)