implements Elegance {

// Elwyn Malethan's musings on software development, mountain biking and general navel–gazing...

Articles tagged with 'dev'

If the users aren't motivated to help with software development...

If the users aren't motivated to help with software development by answering questions or reviewing the results of a sprint, then the software isn't creating any value. Stop work now and find something the users really want. — Stephen Lott

The number of times I‘ve wanted to do that! Some customers I‘ve worked with are really averse to engaging in the development process. However, it makes those customers that do eagerly engage in the process all the more a delight to work with. Guess which customers had most success with their projects…

First published on Feb 24, 2010. Last updated on: Feb 24, 2010.

Why TDD: Freedom to refactor

Dave and Jen work for software companies that produce exciting and clever software products. Both Dave and Jen understand design patterns. They both know about encapsulation, inheritance, composition and all that jazz. They‘re both good software developers. The only difference is that Jen has taught herself to develop test-driven. Dave thinks his time is better spent writing production code.

Dave and Jen started their current project at the same time. Dave spent some time up–front to carefully design his architecture to the requirements and implemented all the features. Dave added some unit tests, but only as an afterthought. Dave is confident that his architecture is well suited to providing a maintainable solution to the requirements specified. Dave is OK with the fact he has less than 25% test coverage.

Jen used TDD, she started off by adding some tests to represent some of the requirements and implementing production code to pass those tests. She did this iteratively and in small digestible chunks until she had satisfied all of the requirements. Jen changed the architecture of the project frequently. She refactored as she went, renaming classes and methods so that they better represented their function, moving code and responsibilities between classes to continually improve the design of the software. She did this safe in the knowledge that her tests would pick up any errors introduced. Jen builds up test coverage of over 80%.

As always happens in a software project, the requirements change. In Dave‘s case, management decided that – for political reasons – Dave needed to completely change the way his product worked. Jen‘s customer‘s priorities changed. To stay competitive, they needed to have different features. In both cases – as ever – time was short and deadlines were tight.

Both Jen and Dave found that their existing design wasn‘t really suited to the new requirements. Dave was concerned about the deadline and the time it would take to evaluate the correctness of any change he made in the design of the software. He has no timely or repeatable means of doing this. So Dave added the feature without changing the design. He knew it wasn‘t ideal and that the he would need to change the architecture at some point in the future. Given the time constraints, Dave believed he was being pragmatic.

Jen – on the other hand – had been refactoring the code extensively throughout the development of the project. Jen recognises an alternative design that would elegantly accommodate this new requirement and refactors. She runs the tests to see if she broke something. A few of them fail. So she fixes them and runs the tests again, safe in the knowledge that her tests represent the requirements.

Over the next few years a number of new features are requested by both Dave‘s and Jen‘s customer(s). Each time, Dave opts for the change that least affects the existing code-base. Each time Dave‘ project becomes more like a ball of mud. Dave becomes more and more resistant to new features. He started asking management for some “engineering time“ to refactor the code and have the time to evaluate it‘s correctness. This – he knew – would take some time.

In the same time, Jen‘s team have been doing nothing but implementing new features. The design of the software has changed frequently in that time. She doesn‘t need any “engineering time”, her tests evaluate the correctness of every feature she adds.

Dave has become scared of changing the code, he doesn‘t know what he might break.

Jen breaks the code every day. She‘ll know in seconds what she‘s broken and what she needs to do to fix it.

The design patterns link above is an affiliate link. Any and every software developer should have a well–thumbed copy of this book.

First published on Jan 10, 2010. Last updated on: Jan 10, 2010.

TDD in JavaScript intensive software

I recently switched jobs. I left Smartstream for somewhere closer to home. I did this primarily to avoid the commute from Cardiff to Aztec West, in Bristol. I'll be glad to avoid that drive on the M4 every day. I joined Move, who are based in Utah but have a development office not too far from Cardiff.

Having been working in the familiar world of server–side Java before Christmas, I‘ve now moved to the team that work on what we call the “middleware”. Primarily, this is the client software that runs on set–top boxes or web browsers. This is therefore almost entirely JavaScript.

Developing software that is primarily JavaScript is a new experience for me and one I‘m relishing, if with some trepidation. After a bit of Googling, I‘ve basically concluded that there isn‘t vast support for TDD for JavaScript development. This is especially true of IDE support. One thing that does look promising is JsTestDriver, though I‘ve been having trouble getting it to work reliably. This is most likely lack of familiarity with both JavaScript and JsTestDriver.

This is challenging stuff, but that‘s great as far as I‘m concerned.

First published on Jan 5, 2010. Last updated on: Jan 5, 2010.

Pingback Java API

A couple of weeks ago I set about implementing Pingback 1.0 for this website. The first logical step was trying to see if there was an existing open–source project for Java implementations of Pingback or whether there were any easily reusable examples to borrow on the intarwebz.

My searches came up dry, so I rolled my own. And, so I have something to write about too! Here‘s how I did it.

The first thing I looked at was identifying links to other pages within the articles of my site. There are loads of regular expressions claiming some degree of effectiveness at identifying URLs, I chose a regex based on this one.

/**
 * This regex checks for both Textile ("blah":url) and HTML (<a href="url">blah</a>) links.
 */
public static final String URL_REGEX = "((\":)|href=\")((http(s?)\\:\\/\\/|~/|/)?((\\w+:\\w+@)?(([-\\w]+\\.)+(com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))|localhost)(:[\\d]{1,5})?(((/([-\\w~!$+|.,=]|%[a-f\\d]{2})+)+|/)+|\\?|#)?((\\?([-\\w~!$+|.,*:]|%[a-f\\d{2}])+=([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)(&([-\\w~!$+|.,*:]|%[a-f\\d{2}])+=([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)*)*(#([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)?)"

Regular expressions are incomprehensible when they get that big! And, this is probably overkill and too specific (e.g. it will ignore any new top–level–domains that are invented).

Anyway, my regex matches any URLs that are either the value for a href attribute in HTML (e.g. href="some_url"...) or part of Textile markup ("Some label":some_url). I check for both because Textile allows for HTML to be embedded so just parsing links in Textile won‘t do. Also this allows me to use the same regex to check remote pages for links to my own site for Pingback server compliance as I use to check my own Textile articles for outgoing links.

The codebase for the API has turned out to be quite tiny. I wouldn‘t say that it is innovative but it certainly re-usable and should simplify the task for anyone who is trying to do the same as I have done. The source is available here. It's in the form of a maven project, so if you want to build it, just type mvn install .

Pingback Client

It is for implementing Pingback clients that this API provides most value. This is the case because I found it very difficult to decouple the Pingback server functionality from the specific implementation of my website. That‘s the subject for another post, however.

Here‘s how the code in my website uses the API.

package com.malethan.blog.app;

import com.malethan.blog.RequestUtil;
import com.malethan.blog.models.BlogPost;
import com.malethan.pingback.Link;
import com.malethan.pingback.PingbackClient;
import com.malethan.pingback.LinkLoader;
import com.malethan.pingback.PingbackException;
import com.malethan.seemorej.AfterFilter;
import static com.malethan.seemorej.SeemoreJ.*;
import static com.malethan.seemorej.Flash.*;
import com.malethan.seemorej.hibernate.crud.CrudControllerHibernate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.beans.factory.xml.XmlBeanFactory;

import java.util.List;
import java.util.ArrayList;

/**
 * <p>The controller for managing articles/blog posts</p>
 */
public class BlogPostController extends CrudControllerHibernate<BlogPost, Long> {
    //----------------------------------------------------------------------- Static Properties and Constants

    private static final Log log = LogFactory.getLog(BlogPostController.class);

    //----------------------------------------------------------------------- Static Methods
    //----------------------------------------------------------------------- Instance Properties

    XmlBeanFactory beanFactory;
    PingbackClient pingbackClient;
    LinkLoader linkLoader;
    List<String> failedPings;

    //----------------------------------------------------------------------- Constructors

    public BlogPostController() {
        super(BlogPost.class, Long.class);
        beanFactory = new XmlBeanFactory(new ClassPathResource("/applicationContext.xml", getClass()));
    }

    //----------------------------------------------------------------------- Filters

    @AfterFilter(include = {"create", "update"})
    public void createOutgoingLinks() throws Exception {
        BlogPost blogPost = (BlogPost) request().getAttribute(modelNameLower);
        if (blogPost.isPublished()) {
            initialiseClientAndLinkLoader();
            failedPings = new ArrayList<String>();
            for (String linkAddress : linkLoader.findLinkAddresses(blogPost.getBody())) {
                if (!blogPost.hasOutgoingLinkToUrl(linkAddress)) {
                    loadRemotePageAndSendPingbacks(blogPost, linkAddress);
                }
            }

            dao.saveOrUpdate(blogPost);

            if (failedPings.size() > 0) {
                notifyUserOfBadPingbacks(failedPings);
            }
        }
    }

    //----------------------------------------------------------------------- Actions
    //----------------------------------------------------------------------- Getters and Setters
    //----------------------------------------------------------------------- Instance Methods

    private void initialiseClientAndLinkLoader() {
        pingbackClient = (PingbackClient) beanFactory.getBean("pingBackClient");
        linkLoader = (LinkLoader) beanFactory.getBean("pingbackLinkLoader");
    }

    private void loadRemotePageAndSendPingbacks(BlogPost blogPost, String linkAddress) {
        Link link = linkLoader.loadLink(linkAddress);
        if(link.isSuccess()) {
            if(link.isPingbackEnabled()) {
                sendPingback(blogPost, link);
            } else {
                blogPost.addOutGoingLink(link.getTitle(), link.getUrl());
            }
        }
    }

    private void sendPingback(BlogPost blogPost, Link link) {
        try {
            String permaLink = RequestUtil.getAppURL(request()) + "/article/" + blogPost.getSlug() + ".html";
            pingbackClient.sendPingback(permaLink, link);
            blogPost.addOutGoingLink(link.getTitle(), link.getUrl());
        } catch (PingbackException e) {
            log.error("Pingback to '" + link.getUrl() + "' failed", e);
            failedPings.add("Pingback to " + link.getUrl() + " failed because of " + e.getMessage() + " ");
            if (PingbackClient.PINGBACK_ALREADY_REGISTERED == e.getFaultCode()) {
                blogPost.addOutGoingLink(link.getTitle(), link.getUrl());
            }
        }
    }

    private void notifyUserOfBadPingbacks(List<String> failedPings) {
        String errMsg = "";
        for (String failedPing : failedPings) {
            errMsg += failedPing;
        }
        flash(NOTICE, errMsg);
    }
}

The annotation  @AfterFilter(include = {"create", "update"}) causes the method createOutgoingLinks() to be invoked after an action is invoked if the action is called create or update. Those two actions are part of the SeemoreJ CRUD framework (that's another post as well, if I ever get around to it). Hopefully, it's easy to make out what's going on. Essentially, if a post is published it, looks for all links with fully qualified URLs in the article and – if the remote resources support it – attempts to send them a pingback. Any failures are displayed using a Rails–style flash system.

I‘ve noticed that this chugs a little if there are a few links in a page and/or certain resources are slow loading. Still, it‘s not publicly visible so I‘ll deal with it for the moment.

Also, this example uses Spring to load the default implementations of LinkLoader and PingbackClient both interfaces defined in the library. It would work just as well with concrete instantiations. Though, it would be more difficult to test :)

It was certainly fun to write, I hope somebody finds it useful :)

First published on Mar 20, 2009. Last updated on: Dec 30, 2009.

 
People I like
Other sites