Testing Liquid Blocks With assert_select

I’ve been looking at trying to write some Liquid tags for use as Mephisto plugins. I still have my doubts as to how productive it is to extend/work with Liquid, but at least I’m making some small headway.

I’d gotten as far as having some code working (there were no errors in the running of the test) but I needed to test more deeply: that I could actually render something useful.

To me, tests are essential as a means of proving I’m making progress, and actually writing code that works. This is even more useful in a relatively new environment (read: Mephisto and Liquid).

Thankfully, Rails already includes some great stuff for testing HTML templating (and our controllers), especially now with the recent addition of assert_select to core. By default, it works by reading the respone inside your functional and integration tests via Rails’ @response object. However, it will also work with an HTML::Node so to test our Liquid block renders correctly we just need to create an HTML document from it!

Our test looks something like this




12345678910
def test_should_see_one_image_in_template_results      template = Liquid::Template.parse(        "{% flickrphotostream %}          <img src='' alt='{{pic.title}}' />        {% endflickrphotostream %}"        )      root_node = HTML::Document.new(template.render).root      assert_select root_node, 'img[alt=my title]'    end

We assert that we find an image tag that with some alt text set to our picture’s title. All nice and simple.

I’ll work on getting the rest of the code written, now I have a way to test it! All that’s left is to beautify it up a little - that’s too much hook-up code I’ll need in each test for my liking :)

Relative Dates with Scribbish in Mephisto

By default, the theme I’d been using with Mephisto displayed dates in a relatively obtuse way, at least for a blog.

One of the things I’ve learned to appreciate more when reading blogs is that by looking at the published date, I can determine how recently it was updated, and, whether it’s a blog that’s full of life. Very much unlike this one of late :)

Although just putting the published date gives me that information, it’s more than I really need. Actually, all I’m interested in is how recent the post was. Not when the post was precisely made. That has a use, but as a regular surfer to the site I don’t mind. In my RSS client (NewsFire), the important thing is whether it’s a new post.

The same data, different consumption scenarios, different priorities.

I wanted to get relative dates working again. After all, Typo used to support that for most of the themes I’d tried. So, rather than displaying the whole date, I’d like it to display ‘3 days ago’, for instance.

Turns out, the Scribbish theme for Mephisto already includes this support. It has a scribbish.js JavaScript file that includes a function to show_dates_as_local_time.

Sweet. Everything’s already there. Almost.

All that’s needed is to add a span element to the liquid template (with it’s class set to scribbish_date) that includes the expected date format (Tuesday, November 21, 2006 14:57:00 GMT - for example), that is then replaced by the JavaScript. That way, the markup contains a full date and time.

So, inside the _article_author.liquid template, look for the following:

<abbr class="published" title="{{ article.published_at | date: '%Y-%m-%dT%H:%M:%S+00:00' }}"></abbr>

Change it to

<abbr class="published" title="{{ article.published_at | date: '%Y-%m-%dT%H:%M:%S+00:00' }}">  <span class="scribbish_date" title="{{ article.published_at | date: '%A, %B %d, %Y %H:%M:%S %Z' }}">  {{ article.published_at | date: ‘%A, %B %d, %Y %H:%M:%S %Z’ }}  </span></abbr>

The “%A, %B %d, %Y %H:%M:%S %Z” format string ensures the date gets rendered correctly (Tuesday, November 21, 2006 14:57:00 GMT) and when you browse to the page, the date gets replaced. Magic.

Equality and the Domain

If you’re test driving your code, and you find yourself writing `assertEquals` you’re making a fundamental statement about your system: that the two objects you’re dealing with are equal (whatever that means). So, testing for equality with `assertEquals` is just. Whether you’re using the `equals` method correctly, however, depends on whether you’ve understood your domain correctly.

After I left a comment, George emailed me with a follow-up to his post with a further example:


In my application, I have two instances of Employee: Tony and Larry. Let’s assume, that in the business context, the definition of equality is defined based only on the yearsOfExperience attribute.


That means, I could do assertEquals( tony, larry ), and if Tony and Larry have the same years of experience, the test will pass.


However, Tony has a different name and a different age than Larry, so the test could potentially be wrong, if what it was supposed to be testing was that it’s the instance of Larry that I’m passing around.

In the above statement of our domain (the business context), we have identified an `Employee` that has a single attribute, that from it’s naming, doesn’t appear to be unique. It’s not an identifier.

The thing is, most of the times we use the term `Employee` it’s because we’re interested in being able to identify a particular Employee, rather than just having a set of employees that we record experience with. So, the example uses language that sits a little uneasy, but more on that in a second.

But, if we are saying that years of experience is all that’s important then of course `equals` would only need to check whether `yearsOfExperience` are equal to determine the equality of two employees. But, the language of the domain is a little misleading. This is what George mentions when he says “if what it was supposed to be testing was that it’s the instance of Larry that I’m passing around”.

Of course, we do hit problems if it’s the actual instance we’re trying to check is the same. But, then our test would be wrong and our statement of intent would be wrong in asserting equals. This would be an example of misunderstanding the domain, as much as anything else.

Domain Driven

Domain Driven Design gives us great terminology to discuss these things (much better than any other thing I’ve read on the subject). It categorises domain objects into two categories

1. Entities. These represent objects in our system where (unique) identity is important in our system, where their lifecycle and continuity are important – their persistence etc.
2. Value Objects. Immutable objects, where it’s attributes are important.

In the example George emailed to me, the code would indicate the `Employee` should be classified as a Value Object. Since, the only thing we know of that we care about is `yearsOfExperience`, and importantly, we make no statements about it’s uniqueness.

But, because we’ve named it `Employee` we’re immediately using language of a familiar domain – that the `Employee` class represents someone that’s important to us – rather than just an attribute holder. On face value, the object is a Value Object.

Chances are though, what we’re actually talking about is an `Entity`. In which case, testing for equality on `yearsOfExperience` is wrong. And it’s wrong within the context of our system – our domain. Instead, we need to identify what we should do to identify the object.

Importantly, our tests are revealing something about our code that we hadn’t previously thought about.

Our tests have revealed that our Employee is probably an Entity, rather than a Value Object. This could be verified by us writing future tests that don’t expect two employees (that share the same `yearsOfExperience`) to be equal. In which case, we would have to introduce some kind of natural key (or other unique identifier) that makes sense to identify the object, and thus determine equality.

By us implicitly using `equals` to make a statement about our objects, we’ve highlighted that we have an Entity that’s being treated as a Value Object.

Of course, tests can also show up where we have a Value Object that’s being held in aspects of another class.

I was working on some code today with Stuart Caborn, another brilliant member of the ThoughtWorks team (and co-author of the testing anti-patterns with George). We were trying to make sense of some code, whilst discussing this (and some other Domain Driven Design stuff). We suddenly noticed something.

We had a lot of Entities in this part of our code, but not so many Value Objects. Or at least, we had some around the rest of the system but they weren’t being used consistently, or widely. Some of our tests were doing something along the lines of

public void testShouldBuildCorrectPaymentFromPaymentSpecification() {  ...  assertEquals("GBP", payment.getAmountCurrency());  assertEquals("10.00", payment.getAmountValue().toString());  assertEquals("Addr1", payment.getBeneficiaryAddress1());  assertEquals("Addr2", payment.getBeneficiaryAddress2());  assertEquals("Addr3", payment.getBeneficiaryAddress3());}

Ok, ignoring any issues with our code as it is. The thing is we are testing equality here. Whether we admit it or not (for now), we are trying to test equality for aspects of our Entity. This smelly test code has revealed that we have some Value Objects that do exist in our domain, it’s just we hadn’t identified them before.

Instead, we should be looking towards writing something like

public void testShouldBuildCorrectPaymentFromPaymentSpecification() {  ...  assertEquals(expectedAmount, payment.getAmount());  assertEquals(expectedAddress, payment.getBeneficiary().getAddress());}

and we’d move some of our other assertions into other tests

public class MoneyTest extends TestCase {  public void testShouldConsiderMoneysWithSameCurrencyAndQuantityEqual() {    assertEquals("GBP", amount.getCurrency());    assertEquals("10.00", payment.getQuantity().toString());  }}

We’ve introduced the value objects into our domain, has made better use of encapsulation, and aligned our code against the Ubiquitous Language. Our code has become more expressive of our model, and provided a deeper model for developers to work with.

Our model has been extended and enriched by test-driving it.

It’s surprising, but it’s amazing how these kinds of insights only come from taking a step back and looking at what gives you pain. And that’s why I love tests and pairing. You’ve gotta love TDDDD (I just made that up, probably an acronym too far :).

Washed out colour from RAW with Apple Aperture - Help!

I’m back from holiday, having spent nearly a week in lovely Paris and am in the process of getting some shots up onto Flickr but am hitting a few problems. Chiefly, the colours in the exported JPEGs (from RAW) are appearing washed out when viewing in Camino, but not Safari.

I spent a day or two earlier this week in Aperture making tweaks to the photos, picking the shots to keep, rejecting the ones that were quite plainly rubbish.

I’m a relative newcomer to Aperture, and to shooting in RAW. The additional flexibility I get in adjusting white balance (I frequently forget to adjust it on my camera) and other similar fixes after the fact are invaluable to me as a pretty average photographer.

I also love using Flickr. I’m a member of the Nikon D50 Group (the DSLR I use – it’s fantastic), and love to just nosey around when I get down time, it’s a great way of getting inspiration for things to do, see, and take photos of.

I’m now trying to get to grips with my workflow in taking photos all the way to Flickr and PhotoBox (my printer of choice) and the colours in Camino look very washed out:

They were exported using the sRGB ColourSync profile, and I’ve also read posts saying that Aperture even implicitly converted every JPEG to sRGB. So, as far as my limited understanding tells me, I’m doing all I need to have the correct colours in place for images to display bright, vivid, delectable colour. Is it just Camino doing something stupid? Or are the images actually rubbish.

So, trusted lazyweb, do people have suggestions for what to do? Any other Aperture/Flickr addicts with tips?

All this malarkey just days after recommending it to another TW-er – Sam Newman. D’oh.

Anyway, many thanks in advance!

Another Day, Another Server

Well I’m not so sure that Lighty, and FCGI running Rails and PHP isn’t such a great mix after all. After approximately a day all goes a little crazy and everything becomes largely unresponsive. So I’m taking drastic measures, and trying yet another web server.

This time, I’m giving Litespeed a go. It’s a commercial piece of software, but, they do offer a standard edition that’s free. So far it seems to run pretty good, importantly it seems to do a good job of serving PHP (which always seems to have been on the problem on my server anyway – I had some PHP apps for stats tracking and webmail).

After a while I’d have `defunct` php processes kicking around, and fairly soon after my Rails processes would go crazy. Then I was in a world of pain, which most certainly did not rock!

I’ll keep an eye on things. So far it seems pretty stable, and was very easy to setup (following their own guide). I just need to get a few more things configured and I’m all ported.

I’d definitely be tempted to switch back to Lighty, but I think I’ll have to wait until 1.5.0 and `mod_proxy_core`. Also, have any people on the lazyweb had problems with Mongrel 0.13? I tried Lighty -> Pound -> Mongrel and that also seemed to die as often as Lighty -> Mongrel?

Sorting Related Articles by Relevance

Sort By Frequency On Array

To make the code easier to write and test, I decided to roll a `sort_by_frequency` method that I could add as an instance method to `Array`. I initially started with the tests inside the existing plugin tests, but, it soon became difficult when trying to do




1234
assert_equal 3, article.related_articles.size1.upto(3) do |n|  assert_equal thoughtworks_articles[n], article.related_articles[n]end

... since Rails would dump an inspection of the Articles that didn’t match, making it rather difficult to figure out what’s going on.

So, I started simpler, and added a new test into the plugin’s test directory, and wrote the following




123456
def test_can_sort_by_frequency_for_numbers  unsorted = [2, 1, 2, 3, 4, 3, 4, 3, 4, 4]  expected = [4, 4, 4, 4, 3, 3, 3, 2, 2, 1]      assert_equal expected, unsorted.sort_by_frequencyend

Running the test showed that the `Array` class didn’t contain a `sort_by_frequency` method, so I went and added a module and (like with the `related_articles` method) injected into the Array class




Array.send :include, SortByFrequency

To get the test up and failing meaningfully, I also then just returned `self` inside `sorty_by_frequency`. Finally, a failing test – we’re expecting an array sorted by the frequency the object occurred inside the original array. Instead, we’re just getting the original array back.

So, to implement I wrote




1234
def sort_by_frequency  histogram = inject(Hash.new(0)) {|hash, element| hash[element] += 1; hash}  sort {|x, y| (histogram[x] <=> histogram[y]) * - 1}end

The code works as follows. Firstly, it creates a histogram – which records the number of times the value inside the array has occurred. The histogram is implemented as a `Hash` which uses `Enumerable’s` `inject` method to create itself.

Using `inject` reminds you of just how painful dealing with arrays, lists, sets, and collections is in other languages (read Java, C# and others).

It works by passing the parameter to `inject` as the first argument to the block. The second time it’s called, it passes the result of the block’s previous execution. So, the `hash` is retained across block invocations – it’s nice to avoid having to declare the `histogram` separately! The second parameter to the block is the element from the array – from our original test, the numbers.

Once the histogram has been created, it’s time to then sort ourselves based on it’s contents. To do so, we use `Enumerable`’s `sort` method which uses the result from the block to determine how to sort the array. We compare the frequency counts inside the histogram for both sides of the sort (`x` and `y` parameters to the block being elements from our array). We then multiply the result of the comparison by -1, this way, it will invert the results so the most frequent are first.

If we run the test again we’ll get a green-ish bar (love TextMate’s new colouring of test results). That’s given me some confidence that we’ve written the sort code correctly, but I just want to make sure the method works fine with Typo article’s also. So, let’s also add a test




12345678910
def test_can_sort_articles  a1 = Article.new()  a2 = Article.new()  a3 = Article.new()  unsorted = [a2, a1, a3, a2, a3, a3]  expected = [a3, a3, a3, a2, a2, a1]  assert_equal expected, unsorted.sort_by_frequencyend

Now all we need to do is go to our `related_articles` tests, and add a test to show we’re sorting related articles by relevance.

Sorting Related Articles by Relevance

To test that we can order the results by the number of tags they share, we need to create some test articles. Rather than adding them through Rails’ fixtures (which I’ve found to be a bit of a pain) instead we’ll create them inside our test.

We’ll create four articles. The first article will be what we consider to be the article we’re currently viewing. The others will have a different number of shared tags each. My test looks as follows




123456789101112131415161718192021
def test_can_order_by_number_of_overlapping_tags  article = create_valid_article(:keywords => 'thoughtworks agile development')      # Create a sliding scale of articles, all of which share the 'thoughtworks'  # tag. 2 will share the 'agile' tag, and 1 will share the development tag.  thoughtworks_articles = []  1.upto(3) do |n|    if n == 1      thoughtworks_articles << create_valid_article(:keywords => 'thoughtworks agile development')    elsif n < 3      thoughtworks_articles << create_valid_article(:keywords => 'thoughtworks agile')    else      thoughtworks_articles << create_valid_article(:keywords => 'thoughtworks')    end  end      assert_equal 3, article.related_articles.size  1.upto(3) do |n|    assert_equal thoughtworks_articles[n], article.related_articles[n]  endend

If we run the test now, it will fail so we need to change the code as follows




1234567891011121314
def related_articles(*options)  related_articles = []  self.tags.each do |tag|    tag.articles.each {|article| related_articles << article if can_add_related?(article, related_articles)}  end      related_articles = related_articles.sort_by_frequency.uniq  ...endprivatedef can_add_related?(article, related_articles)  article.published and article != selfend

Note that we’ve also changed `can_add_related?` from the previous iteration (from the code I put in a previous post), since we now need to have an array containing duplicate entries – the number of times an article appears is used as the index to the array. We exclude duplicate entries once we’ve sorted, using the `uniq` method.

Running the tests now and we’ll see the tests pass!

Refactoring

My last post attracted a few suggestions from Piers Cawley (one of the Typo developers), so I thought I’d take a look to try and learn a few things.

Firstly, I changed my related articles test to the following




123456
privateVALID_ARTICLE_DEFAULTS = {:user_id => 1, :body => 'foo', :title => 'bar', :keywords => 'testing1', :published => true}def create_valid_article(*args)  options = Hash === args.last ? args.pop : {}  Article.create! options.reverse_merge(VALID_ARTICLE_DEFAULTS)end

The change uses Rails’ core extensions for Hash – `reverse_merge` which replaces anything inside our `VALID_ARTICLES_DEFAULTS` with those set on our `options` argument. It also uses Rails’ helper method `create` which both creates the in-memory object, and saves it to the database. Typo has a number of filters which handle when the `ActiveRecord` object is created/saved so the `create` method can be seen a factory method that sets up the `Article` in a good state.

I’m also not so keen on the length and structure of `related_articles` in `RelatedArticlesSearch` module. Let’s see if we can make some small refactorings to improve the code.

Firstly, let’s extract out a method for the finding of all related articles before we do the rest of the processing




1234567891011121314151617181920
def related_articles(*options)  related_articles = find_related_articles.sort_by_frequency.uniq      if options.length == 0    related_articles  else    params = options[0]    count = params[:limit] - 1    related_articles[0..count]  endend   privatedef find_related_articles  related_articles = []  self.tags.each do |tag|    tag.articles.each {|article| related_articles << article if can_add_related?(article, related_articles)}  end  related_articles    end

We introduced the `find_related_articles` method – to do the database search. Re-run the tests to make sure we can run and we’re happy to go on. Cool, let’s see if we can reduce the number of lines inside our `find_related_articles` method. How about we use the `inject` method again to create our Array




12345678910111213141516171819
def related_articles(*options)  related_articles = find_related_articles.sort_by_frequency.uniq      if options.length == 0    return related_articles  end      params = options[0]  count = params[:limit] - 1  related_articles[0..count]endprivatedef find_related_articles  self.tags.inject(Array.new) do |related_articles, tag|    tag.articles.each {|article| related_articles << article if can_add_related?(article)}    related_articles  endend

Neat. So, we create our result array when we start iterating over our `tags`, and then add items to it as we go. Although this works, we could probably wrap up the appending of items to the array using one of the other Ruby iterators.




123456
privatedef find_related_articles  self.tags.inject(Array.new) do |related_articles, tag|    related_articles + tag.articles.collect {|article| article if can_add_related?(article)}.compact  endend

This time we use the `collect` method to return an Array of elements we’d like to add to our other array. Before we append them using the `+` operator, we call `compact` to remove any `nil` entries first. Much nicer.

Now, let’s turn ourselves to the `related_articles` method, let’s first get rid of the large conditional and instead do




1234567891011
def related_articles(*options)  related_articles = find_related_articles.sort_by_frequency.uniq      if options.length == 0    return related_articles  end      params = options[0]  count = params[:limit] - 1  related_articles[0..count]end

Looks a little neater, but that `:limit` stuff still looks messy. How about we use




12345678
def related_articles(*options)  params = Hash === options.last ? options.pop : {}      related_articles = find_related_articles.sort_by_frequency.uniq      count = nil != params[:limit] ? params[:limit] : related_articles.size      related_articles[0..count - 1]end

Better, but still reads a little weirdly. Let’s try a little more, perhaps we should go with




12345678
def related_articles(*options)  params = Hash === options.last ? options.pop : {}      related_articles = find_related_articles.sort_by_frequency.uniq  count = params[:limit] ||= related_articles.size      related_articles[0..count - 1]end

Much nicer.

Getting the Code

The code for the `Article::related_articles` method is available as a Rails plugin, and can be installed by running:

$ script/plugin install http://www.engross.org/svn/typolatest/trunk$ mv vendor/plugins/trunk vendor/plugins/related_articles_search

or, you can also check it out (via Subversion) as follows

$ cd vendor/plugins$ svn co http://www.engross.org/svn/typo/typolatest/trunk related_articles_search

Once you’ve done that, you can download the Sidebar code. This will currently only work with non-edge Typo (i.e. the latest fully released version) as I’ve not written a Sidebar component for the new architecture. I’ll be working on that next :)

To get the Sidebar component you can download the component in a ZIP archive. Then, unzip it inside your `components/plugins/sidebars` directory. You should end up with a `components/plugins/sidebars/related_articles` directory.

Of course, you can also browse the code through Trac at http://dev.engross.org/trac/typo/browser. Hopefully I’ve not made quite such a mess of the code as before, but if I have, please point out where I can improve.

The Importance of Language

I needed to write a new test that would prove that I had unique entries in an array and I’d long grown tired of adding items through Rails’ fixtures. I wanted to create a number of articles in my test that I could use as the sample data. My first shot was roughly as follows:




123456789101112131415161718192021222324252627282930
def test_can_load_unique_articles  number_of_iterations = 8   existing = Article.find_all.length      article = create_articles(number_of_iterations)  assert_equal number_of_iterations - 1, article.related_articles.sizeend  def create_articles(number_of_iterations)  test_article = nil  1.upto number_of_iterations do |n|    a = create_valid_article    a.save!          test_article = a  end    test_articleenddef create_valid_article  a = Article.new()  a.user_id = 1  a.body = "Foo"  a.title = "Zzz"  a.keywords = 'testing1 testing2 testing3'  a.published = true        a.keywords_to_tagsend

It’s a pretty beastly piece of Ruby code, but it worked. I then took a stab at tidying it up, after a few iterations of smaller refactorings I ended up with:




1234567891011121314151617181920212223242526272829
def test_can_load_unique_articles  number_of_iterations = 8  existing = Article.find_all.length         articles = create_articles(number_of_iterations) do     create_valid_article  end      assert_equal number_of_iterations - 1, articles.first.related_articles.sizeend    privatedef create_articles(number_of_iterations)  created = []  1.upto number_of_iterations do |n|    a = yield          a.keywords_to_tags    a.save!          created << a  end  createdend  def create_valid_article(*args)  options = Hash === args.last ? args.pop : {}      defaults = {:user_id => 1, :body => 'foo', :title => 'bar', :keywords => 'testing1', :published => true}  Article.new(defaults.merge(options))end

Ah, that’s a little more Ruby like, and definitely feels a lot more succinct. More importantly, it does so without becoming too complicated to understand and it raises the importance of what I’m creating into the test method. For me it’s equivalent to why I dislike putting JMock expectations anywhere other than inside the test—clarity!

What’s even more remarkable is I ended up there by trying to make it easier to re-use the code and create different types of articles in bulk for other tests I was writing. Almost by accident, and by taking small baby steps, I refactored to a drastically more communicative test.

I mention all this because I also just today read a brilliant, if somewhat flamebaiting, post about how Java’s slant had actually hurt it’s ability to communicate a model by shifting emphasis away from verbs into nouns, with many ‘verbs’ in the domain becoming wrapped by ‘noun’ classes:


The King [of the Kingdom of Java], consulting with the Sun God on the matter, has at times threatened to banish entirely all Verbs from the Kingdom of Java. If this should ever to come to pass, the inhabitants would surely need at least one Verb to do all the chores, and the King, who possesses a rather cruel sense of humor, has indicated that his choice would be most assuredly be “execute”.


The Verb “execute”, and its synonymous cousins “run”, “start”, “go”, “justDoIt”, “makeItSo”, and the like, can perform the work of any other Verb by replacing it with an appropriate Executioner and a call to execute(). Need to wait? Waiter.execute(). Brush your teeth? ToothBrusher(myTeeth).go(). Take out the garbage? TrashDisposalPlanExecutor.doIt(). No Verb is safe; all can be replaced by a Noun on the run.

It’s definitely an interesting point though. Whether Java’s emphasis on objects actually makes code less communicative. The writer made the point that features in other languages (such as closures in Ruby and function pointers in C) meant that it was easier to write code that was focused more on actions and (what he believes) is more communicative code.

What was even more interesting was that today I also just read fellow ThoughtWorker and team-mate, George Malamidis’ post about Closures for an upcoming release of Java.

He raises a very, very good point (and one I believe is more important than the one raised in the previous article) about there being more pressing issues at hand in Java (and probably ‘enterprise’/business software development in general), than worrying about adding things such as Closures:


I’d like to see focus shifted to the core characteristics of Java that made it such an interesting prospect at first, and these, in my opinion, boil down to good software design, powered by fundamental OO principles, not bells and whistles like Continuations, which, according to the Ruby FAQ, can be used to implement complex control structures, but are typically more useful as ways of confusing people.

And, although I agree there are bigger issues, I’m not so sure it’s one that conflicts with language features like the ones I mentioned earlier.

The more I think about things, the more I think of examples where I’ve been on teams that have diverged from attempting to deal with the business model at the heart of the software, and moreso with just being able to get stuff done. The result is (in my opinion) not a problem with verbs being replaced by nouns, but with using bad nouns, and putting them in bad places.

Through making compromises and not exploring the domain deeply and often enough (and collaborating with domain experts more) the code that represents the domain grows into one of those scary gardens that your ball would fall into and you’d play paper, rock, scissors with your friends to decide who had to knock on the door of the owner to ask for it back.

You end up in a situation where the ubiquitous language is still not ubiquitous and knowledge has not been sufficiently crunched for effective working (to borrow from Domain Driven Design’s glossary). Going forward, the code doesn’t help developers understand the domain, and people find themselves making further shortcut hacks to get some breathing space rather than having a potentially clearer path already visible.

I don’t think this problem is one uniquely squared at Java, but I am intrigued to see how different languages allow for different opportunities to solve the same problem in different ways and how that affects a developer’s ability to handle domain complexity effectively.

Going back to the code sample that prompted this post, it used a block to raise the importance of what we were creating into the test. If I were trying to do something functionally (and communicatively) equivalent in Java, I think I’d probably have ended up somewhere around here:




1234567891011121314151617181920212223242526
public void testShouldOnlyIncludeDistinctArticles() {  List articles = createArticles(8, validArticleFactory, articleSpecification);  Article firstArticle = articles.get(0);  assertEquals(7, firstArticle.relatedArticles.length);}private List createArticles(Integer count, ArticleFactory factory, ArticleSpecification spec) {  List articles = new ArrayList();  for (int i = 0; i < count; i++) {    articles.put(factory.createArticleFrom(spec));  }  return articles;}private static class ArticleWithTag1Specification implements ArticleSpecification {  ...  public String keywords = "tag1";}private static class ArticleWithTag2Specification implements ArticleSpecification {  ...  public String keywords = "tag2";}

We’ve created a fair few objects there. And although it is (arguably) just as intentful, it’s certainly more wordy. Going back to Steve Yegge’s post about the Nouns taking over in Javaland. Although this is arguably the case in the above, the naming of the nouns to an extent structure it to describe the domain.

It’s also important to consider that (by definition) giving an action a name is to describe it, that is, we describe a verb with a noun. So the two aren’t completely unrelated :)

But, although the above Java code is explicit with it’s intent and it does provide a domain language focused attempt at describing the problem, I still think the Ruby code is easier and clearer, and perhaps closer to how I (as an English speaking human) would attempt to describe an action.

The bigger question is not whether one language supports funky things like Closures or Blocks (which I do present a risk to over complicating code when developers get too far into the beautifying zone) but is one of how we use the language to address the problems of the domain. And, ultimately, I’d be happier working with code that did that in Java, than code that didn’t in Ruby. But don’t hate me for that :)

Anyway, I’m off to the wine bar to meet some of the team for dinner having received a little too many comments for being unsociable!

Stability Woes

Apologies if you’ve been trying to access one of the posts here over the last few days and received any one of the various Internal Server Error, or Service Unavailable error messages.

It seems that Lighty (`mod_proxy` – I also tried running the latest 1.5.x code from the Subversion trunk to use `mod_proxy_core` but that killed Mongrel everytime), Mongrel and Rails still aren’t playing too well together. Without being able to regularly monitor the server, I’ve not been able to guarantee any kind of uptime.

I’ve reverted back to good ol’ FCGI. So far it seems been pretty good, and I don’t seem to remember having to restart much at all before I went down the Mongrel route.

I can now (finally) get back to adding a ranking (to indicate potential relevance) to the Related Articles plugin I developed for Typo.

Related Articles Plugin for Typo

Getting the Code

The plugin can be installed by either checking out the source code (into `vendor/plugins/related_articles_search`).

svn co http://www.engross.org/svn/typolatest/trunk .

Or, you can use the Rails way:

$ script/plugin install http://www.engross.org/svn/typolatest/trunk$ mv vendor/plugins/trunk vendor/plugins/related_articles_search

Note that the `script/plugin` install goes into the wrong location, I don’t know the best way to fix this just yet, but perhaps I’ll just add an alias inside my web server configuration?

If you just want to browse around the code, feel free to do so from Trac.

Creating the Plugin

The first step was to generate the plugin

$ script/generate plugin related_articles_search

Rails generates some stub files (more convention magic), which we can then move our code into. Of course, we don’t just want to start moving files around, we want to make sure we can still run our tests and prove that the functionality still works. So let’s start by moving those tests into our plugin.

Moving the Tests

We’re attempting to extract the `related_articles` instance method from `Article` into a plugin. That way, we can deploy the update by just installing a plugin and extending code, rather than modifying the existing Typo code (which would also require the user to have an SVN working copy).

So, let’s move the tests from inside `article_test.rb` to `related_articles_search.rb`. Once we’ve done that, let’s run them and see what happens so we know what to try and fix – we’ll guide our work by what the tests tell us. So what do we get?

NoMethodError: undefined method &lsquo;fixtures&rsquo; for RelatedArticlesSearchTest:Class

This is because we need to tell Rails to load our application, all of our model classes, and the various Rails test helpers (including the `fixtures` helper). So, we change the top of the test to look as follows (the key is the second line – despite it’s path ugliness :))




123456789
require 'test/unit'require File.dirname(__FILE__) + '/../../../../test/test_helper'class RelatedArticlesSearchTest < Test::Unit::TestCase  fixtures :contents, :articles_tags, :tags    def test_returns_no_related_articles    assert_equal 0, contents(:article3).related_articles.length  end

Although we no longer get the same message, we still need to move our fixtures over so they’re contained within the Plugin.

Moving the Fixtures

Since this is an example of work which takes us outside of Rails’ _convention_al comfort zone, time to take a peek into how Rails actually loads the fixtures during tests and then figure out how we’ll do it.

Rails’ test helper mixes in the `fixtures` class method which allows you to leverage some of Ruby’s syntactic sugar when you use




fixtures :contents, :articles_tags, :tags

Looking through the code inside `fixtures.rb` it’s possible to see how it loads the fixtures:




123456
def read_fixture_files  if File.file?(yaml_file_path)    # YAML fixtures    begin      yaml_string = ""      Dir["#{@fixture_path}/**/*.yml"].select {|f| test(?f,f) }.each do |subfixture_path|

It looks for any `*.yml` files inside the `fixture_path`. Brilliant.

This in turn is set via Rails’: `Test::Unit::TestCase.fixture_path=(path)`. So, all we have to do is change the fixtures path inside our `TestCase`. So, let’s go ahead and do that. So, at the top of our test file let’s try and do




1234567
require 'test/unit'require File.dirname(__FILE__) + '/../../../../test/test_helper'class RelatedArticlesSearchTest < Test::Unit::TestCase  self.fixture_path = File.dirname(__FILE__) + '/fixtures'    fixtures :contents, :articles_tags, :tags

That way, we’ll stick all our fixtures stuff into a `fixtures` subdirectory. If we re-run the tests now, we probably ought to see some failures since the files aren’t where they’re expected to be


1) Error:test_can_find_a_limited_result_set(RelatedArticlesSearchTest):Errno::ENOENT: No such file or directory - /Users/pingles/work/typo-latest/vendor/plugins/related_articles_search/test/fixtures/contents    method open in fixtures.rb at line 327

Success! In a kind of failing way. Still, it’s always good to have a failing test first. Let’s go and add those files into a new directory and run again

1) Error:test_can_find_a_limited_result_set(RelatedArticlesSearchTest):StandardError: No fixture with name 'related_article_3' found for table 'contents'    method contents in fixtures.rb at line 480    method test_can_find_a_limited_result_set in related_articles_search_test.rb at line 43

Cool, we now know we’re loading the fixture files correctly (because it can’t find anything within them).

So let’s move the content over from the old fixtures and into these files. (Note that this is too boring to describe, safe to say, we’ve moved some setup stuff into these files. I’ve also made a few test changes so we’re not dependent on Typo’s own fixture contents. You can check it out in the Subversion repository

Run the tests again and we see…

Loaded suite /Users/pingles/work/typo-latest/vendor/plugins/related_articles_search/test/related_articles_search_testStarted.......Finished in 0.522689 seconds.7 tests, 11 assertions, 0 failures, 0 errors

Sweet! So our test (and test assets) are now part of our plugin. Time to do the easy bit next and just move the `related_articles` method.

Moving the Finder

So, let’s cut the `related_articles` method code from `Article.rb` and put it into our module. Our `related_articles_search.rb` file will now look like this




12345678910111213141516
module RelatedArticlesSearch  def related_articles(*options)    related = []    self.tags.each do |tag|      tag.articles.collect {|a| related << a if a.published && a != self }    end        if options.length == 0      related    else      params = options[0]      count = params[:limit] - 1      related[0..count]    end  endend

If we re-run the tests now, we’ll see a number of errors appear because we haven’t mixed in our module yet and the `related_articles` method no longer exists on `Article`:


1) Error:test_can_find_a_limited_result_set(RelatedArticlesSearchTest):NoMethodError: undefined method `related_articles' for #

Mixing in the Module

So, into our plugin’s `init.rb` we add the following




12
require 'related_articles_search'Article.send :include, RelatedArticlesSearch

After reading a few bits about mixing in a module my first instinct was to write




12
require 'related_articles_search'Article.include RelatedArticlesSearch

But, because `include` is a private method, that wasn’t going to work. The answer is to tell `Article` (by way of sending a message) to do it.

If we re-run the tests now we’ll see these all pass and we know that our module is working correctly, and we have a functioning Typo plugin!

To Sum Up

We have extracted our plugin complete with tests and test data. Users should now be able to safely install the plugin and verify it’s installed correctly by running the test, avoiding the need to patch the Typo source code.

So, all that’s necessary to install the Sidebar component now is

1. Install the Related Articles Search plugin that provides the `related_articles` instance method to `Article`.
2. Unzip the `related_articles_component.zip` inside your `/components/plugins/sidebars` directory.

Nice huh? No need to patch Typo code, at least not just yet – I’d definitely like to contribute something though.

Well today’s exercise was much easier than I expected it to be, and it’s nice to find there’s a way to package most of this stuff together. Hopefully other users can get it going successfully, please let me know if you can or can’t!

I’ve taken a look at Piers’ changes for Typo and it definitely looks interesting. I’ll get to work on porting the related articles code to the new sidebar architecture and hopefully get an updated version up on Subversion this evening.

Writing a Typo Sidebar Test First In Rails

Writing the Sidebar

More and more of my traffic (thanks to the wonderful stats tracking of both Mint and Google Analytics is coming from search engines.

So, in an attempt to try and keep visitors hanging around for longer (and to make it easier to see related stuff), I’d like to add a related articles Sidebar component.

For example, if users are on a page that mentions Domain Driven Design, I’d like them to see other articles that also cover Domain Driven Design.

And since Typo already uses tags for describing content, we can re-use the same mechanism. So, for any tags an article has, we can show other articles that share those same tags.

So let’s try writing it more as an XP story to focus our effort. How about…

The Story


As a blog owner, I’d like to make it easier for visitors to find content and related posts. I’d like to see a list of related articles appear in the sidebar when I view an article. I’d like relationships to be indicated through sharing tags.

Writing the Code

This post covers notes from when I was writing the Sidebar, and should cover my thought processes at the time pretty well. I’m definitely learning Ruby a whole lot still, so it was a great exercise for me to learn through. I’ve found also found Test Driven Development a great technique to pick up a new language and API etc. on my first ThoughtWorks project – a Java based banking project.

For anybody who isn’t familiar with Test Driven Development, or working with developer testing, I’d recommend this particularly great podcast from a talk Kent Beck gave around developer testing.

A lot of the tests make use of articles and tags that have been added to the fixtures. I won’t cover these here, but the changes are all inside the Subversion patch file.

The First Test

We want to know that we can find related articles so let’s get stuck in and write some of the unit testing code first. Once we have a list of related articles, then we can figure out how to display them. So inside our `article_test.rb` file let’s add the following




123456
def test_can_find_related_article  first_article = contents(:related_article_1)  related_article = contents(:related_article_2)      assert related_article.related_articles.include?(first_article)end

We’re using the existing `contents.yml` fixture to add some test articles (notably, `:related_article`). I won’t detail the changes here, since they’re mostly quite obvious. Full changes are available in the patch file should you want to see them.

The easiest way to make this test pass (and check our test is working correctly) is to return all articles.




123
def related_articles  Article.find_allend

Great, it passed. But, we only want to display related articles if we do actually have related articles. So, let’s add a test that makes sure that if we have an article with no tags (and therefore no relationship to other articles) we have an empty array.




123
def test_returns_no_related_articles  assert_equal 0, contents(:article3).related_articles.lengthend

If we run the test we’ll see it fail since we’re currently returning all articles. So, let’s test that we can restrict the articles returned to be those just matched by the tag. We can do that by changing our `related_articles` method




12345
def related_articles  articles = []  articles = Article.find_all unless self.tags.length > 0  articlesend

Run again and we’re good to go. Let’s now write a test to ensure that our related articles only includes those which share the same tag and that the first item is our related article (so we don’t find ourselves in the results etc.):




1234567
def test_can_find_only_related_article_with_same_tag  first_article = contents(:related_article_1)  related_article = contents(:related_article_2)      assert_equal 1, first_article.related_articles.size  assert_equal related_article, first_article.related_articles.firstend

If we run the test, we should see it fail since we have too many articles being returned.

1) Failure:
test_can_find_only_related_article_with_same_tag(TagTest) [test/unit/tag_test.rb:80]:
expected but was
.

Excellent. So now we need to write a proper finder method, and as with many things, there a couple of ways we could go forward.

One way, would be to write some custom SQL to do the querying in one hit, but I feel that that would probably take a little longer (and be a little more complex – and unclear) than just navigating the objects and their relationships. So, instead, we’ll write it as part of our `Article`.




1234567
def related_articles  related = []      self.tags.each do |tag|    related.concat tag.articles  end  related - [self]end

If we re-run our test we should see it pass. It’s not the prettiest code, but it works.

Finally, we also want to ensure that we only link to articles that are published, so we’ll need to remove any which haven’t been. Again, let’s write a test




12345678
def test_related_articles_only_includes_published_articles  first_article = contents(:related_article_1)  related_article = contents(:related_article_2)  related_but_unpublished_article = contents(:article4)      assert_equal 1, first_article.related_articles.size  assert !first_article.related_articles.include?(related_but_unpublished_article)end

Let’s run the test and see that it fails because we need the additional `:related_article_6` entry in our fixture. After we add that we should see it fail with too many entries.

So, again, focusing on adding the simplest thing that’ll work, let’s add the code to filter out unpublished articles.




123456789
def related_articles  related = []      result = []  self.tags.each do |tag|    related.concat tag.articles  end  related.collect {|a| result << a if a.published && a != self}  related - [self]end

If we run the test again now we should see it pass successfully. Of course, the code is pretty unattractive and un-Ruby like! So, let’s spend a little time tidying it up before we move on. How about…




1234567
def related_articles  related = []  self.tags.each do |tag|    tag.articles.collect {|a| related << a if a.published && a != self }  end  relatedend

That looks much tidier, and the tests still pass so we know it works. I’m happy with that for the time being, so let’s move on to the next part.

The Sidebar Component

We can now retrieve all articles that share the same tag. Rather than spend more time improving the algorithm for finding related articles, instead we’ll focus on delivering this simpler functionality into the hands of our customer sooner. This is what is often referred to as delivering ‘Vertical’ value: delivering a more complete but thinner story. A lot of what 37signals refer to in their Getting Real book has tie-ins to this kind of approach, focusing on simpler now, rather than waiting for everything.

So, the next step then is to write the Typo sidebar component that’s going to use our list of related articles in a list.

Now there doesn’t seem to be a great deal of content about writing tests for components in rails (read: nothing). But, since they’re supposed to be analogous to partials, but instead for controllers, it seems sensible to place them with our other functional tests. At least for the time being.

So, create a `related_articles_controller_test.rb` in the functional tests directory and let’s set about writing our first test.

The first thing we need to do is make sure we include the test helper (like all other functional tests). We also need to include our related articles controller (that doesn’t exist yet) so we can use it in our test. So, at the top of the file add the following




12
require File.dirname(__FILE__) + '/../test_helper'require File.dirname(__FILE__) + '/../../components/plugins/sidebars/related_articles_controller'

We also want to add all the setup stuff so we can use our controller inside our various tests, so let’s also add that




1234
def setup  @controller = Plugins::Sidebars::RelatedArticlesController.new  @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new    end

Now, on to our first Sidebar functional test.

The First Sidebar Test

We want to know that we can see the related links when we view one of our previously created articles (we’ll re-use the related articles stuff in the fixtures we created earlier for our unit tests).




123456
def test_when_we_have_an_article_we_can_see_related_items  get :content, :contents => [contents(:related_article_1)]      assert_response :success  assert_tag :tag => 'ul', :children => {:count => 2, :only => {:tag => 'li'}}end

Typo uses a `contents` array inside Rails’ `params` object to pass the items we’re viewing around. So, we’ll pass in one of our articles as that contents. We then go on to test that our controller renders successfully, and that we can a list with two items.

If we run the test, we’ll see that we haven’t created the sidebar component source file yet so let’s create the structure for our component. Add a `related_articles` directory under the `components/plugins/sidebars` directory. Then, inside that directory add `content.rhtml`, and finally, add `related_articles_controller.rb` inside the `components/plugins/sidebars` directory.

Once that’s been done, let’s add the various sidebar stub stuff (as well as our `content` action that we’re `GET`ting in our test:




1234567
class Plugins::Sidebars::RelatedArticlesController < Sidebars::ComponentPlugin  description "Displays articles that share the same tag."  def content    #do stuff  endend

If we run the test, we’ll see that we don’t find any lists, so we need to edit our view and wire it up.

So, we’re going to need our `RelatedArticlesController` to give us an array (`articles`) that’ll contain the links we want to show. If we run the test now, our `assert_response :success` will fail since our view can’t find what it needs. The simplest way to fix that is to have the following in our controller




123
def content  @articles = []end

We’ll just use an empty array for the time being. That’ll then mean our second assertion will fail since we also need to see two items (for our article).

So, we’ll add the code to do that.




123
def content  @articles = params[:contents].first.related_articlesend

If we run our test, we’ll see it passes. Onto the next test!

Now, seeing a list is all very well, but we need to be able to click links within that list that’ll take us to the various articles (since we’re trying to drive up the number of views people make when visiting).




1234567
def test_we_can_see_article_links  article = contents(:related_article_2)  get :content, :contents => [article]      assert_tag :tag => 'li', :children => {:only => {:tag => 'a'}}  assert_tag :tag => 'a', :attributes => {:href => article.location}, :content => article.titleend

Firstly, we’ll check our list items have anchor tags to display the links, and then check those links have the correct address in them.

If we run the test, you should see the message ”`no tag found matching :attributes=>{:href=>”/articles/2004/06/01/article-4”}, :tag=>”a”, :content=>”Article 4!”`” appear.

Typo has some helpers for writing links to various bits of content, so we can re-use those to actually generate the tag. So, inside our `content.rhtml` view, add

If we re-run we should see our test pass.

Since Typo uses the `contents` array in the `params` object to pass articles around for all pages we’ll need to check that we only see our sidebar when we’re viewing an article, and not viewing the home page (for instance).




12345678910
def test_when_viewing_home_page_no_related_articles_shown  get :content,    :contents => [      contents(:related_articles_1),      contents(:related_articles_2)    ]      assert_response :success  assert_no_tag :tag => 'ul'end

If we re-run we’ll see our test fail because we can still see our list. So, we’ll need to add some logic to our controller to only provide an articles list if we’re viewing an individual article, otherwise we’ll just leave it with an empty array.




1234
def content  @articles = []  @articles = params[:contents].first.related_articles unless params[:contents].to_a.size > 1end

If we run our tests again we’ll see it pass. Now, that code’s pretty ugly so let’s try to refactor it a bit now that we have some tests to make sure we don’t break anything.

Firstly, let’s try and extract out how we get the current article we’re viewing from the `params` object as follows




123456789
def content  @articles = []  @articles = article.related_articles unless params[:contents].to_a.size > 1endprivatedef article  params[:contents].firstend

Then, let’s run our tests and make sure everything is still passing. Excellent, now it’s not immediately clear why we’re checking the length of `params[:contents]` (other than we don’t fill articles). So, let’s make it a little clearer




1234
def content  @articles = []  @articles = article.related_articles if viewing_article?end

and add another private method




123
def viewing_article?  params[:contents].to_a.size == 1end

Much better. As a little safety net, we’ll also make sure that we don’t consider ourselves to be viewing an article unless the first item is also an article. So, let’s add




123456
def test_unless_first_item_is_an_article_display_nothing  get :content, :contents => [tags(:foo_tag)]      assert_response :success  assert_no_tag :tag => 'ul'end

If we run the test we’ll get a 500 error because we’re assuming that if we have one item in our array it’s an article and that we can get related articles. So, let’s add the code to only consider ourselves as viewing an article when the first item is indeed an article.




123
def viewing_article?  params[:contents].to_a.size == 1 && params[:contents].first.kind_of?(Article)end

If we re-run the tests we’ll see it all run successfully.

Finally, if we don’t have any related articles, we’d prefer it if we didn’t see anything in the sidebar, rather than us having an empty list. Our test:




123456
def test_if_no_related_articles_found_nothing_is_displayed  get :index, :contents => [contents(:article3)], :sidebar => @sidebar      assert_response :success  assert_no_tag :tag => 'div'end

If we run the test, we’ll fail because we’ll still have our `div` containing an empty list. All we need to do is wrap our `div` within the following

0 %>...

Re-run the test, watch it pass, and we can proceed onto the Sidebar configuration aspect of our code.

Sidebar Configuration

The advantage of Typo’s Sidebars are that they allow you to configure them through the administration interface.

We want to be able to configure the maximum number of articles that are shown in the related articles list, and the title that will appear above the list.

Our first test is that our configuration includes a Title, that will appear above the list.




1234
def test_can_configure_title_in_admin  assert_equal "title", Plugins::Sidebars::RelatedArticlesController.fields.first.key  assert_equal "Related Articles", Plugins::Sidebars::RelatedArticlesController.default_config["title"]end

We’re testing that we have a configurable title, and that the default value is “Related Articles”. As usual, run the tests and watch them fail. Onto the implementation (inside our `RelatedArticlesController`):




12
class Plugins::Sidebars::RelatedArticlesController < Sidebars::ComponentPlugin  setting :title, 'Related Articles'

Re-run the tests and we can watch them pass.

Now we want to make sure we can see that title above our list, so let’s write a test that we find an `h3` element with our title in it




123456
def test_can_see_default_title  get :content, :contents => [contents(:related_article_3)]      assert_response :success  assert_tag :tag => "h3", :content => "Related Articles"end

At the moment, the test will fail because our template hasn’t yet had the code added. So, in the interest of getting it passing quickly, let’s do the following:

Related Articles

...        
Our test will now pass, so we know that we’re finding the element correctly. Now let’s proceed to write another test that will break our shortsighted implementation. Here we’ll be using the `index` action within our controller, and providing it with Sidebar configuration. Note that this is different for our previous tests, since this is the first time we’ve needed to test this kind of code. At this point (when I was working) I changed the previous test code to match this.


  123456789
  def test_can_see_configured_title  sidebar = Sidebar.new()  sidebar.config = {:title => 'My Articles'}  get :index, :contents => [contents(:related_article_3)], :sidebar => sidebar    assert_response :success  assert_tag :tag => "h3", :content => "My Articles"end


        
Here we’re also passing through our `:sidebar`, this is the representation of the item’s configuration. So, when we render our component our controller can pull the `title` `setting`. So, let’s add the code inside our view template



...        
If we run the test, we’ll get a failure since our title configuration setting doesn’t exist on our Sidebar component yet. All Sidebars are configured the same way, and nicely (thanks to some refactoring from Piers Cawley a while back)
        
All we need to do is add a setting as follows


  12
  class Plugins::Sidebars::RelatedArticlesController < Sidebars::ComponentPlugin  setting :title, 'Related Articles'


        
If we re-run the test all should pass, and we can write our second configuration test – that we are able to set the number of items we’d like to see in our list. We also want to have a label for our field also – this will appear next to the textbox to indicate what the setting controls.


  12345
  def test_can_configure_number_of_items_in_admin  assert_equal 'count', Plugins::Sidebars::RelatedArticlesController.fields.last.key  assert_equal 5, Plugins::Sidebars::RelatedArticlesController.default_config['count']  assert_equal 'Number of Articles', Plugins::Sidebars::RelatedArticlesController.fields.last.options[:label]end


        
If we run our test, we’ll see a failure because we only have one field
        
<"count"> expected but was<"title">
        
So, let’s add a setting to fix the first assertion


  123
  class Plugins::Sidebars::RelatedArticlesController < Sidebars::ComponentPlugin  setting :title, 'Related Articles'  setting :count, 5


        
If we re-run, we’ll have a failure because our configuration field’s options doesn’t contain a label. So, let’s add that to our setting definition in our controller


  
  setting :count, 5, :label => 'Number of Articles'


        
If we re-run our test, we’ll see it pass!
        
Finally, we need to write the tests to ensure that we limit the number of items we actually see to the number that’s specified in the configuration. Let’s take our example from an earlier test. This time, we’ll say we only want to see one item though (rather than the two that are actually associated):


  123456
  def test_can_see_limited_number_of_items  get :index, :contents => [contents(:related_article_3)], :sidebar => @sidebar      assert_response :success  assert_tag :tag => 'ul', :children => {:count => 1, :only => {:tag => 'li'}}end


        
To do this, we’ll need to add a limiter to our `related_articles` method that can limit the number of results. Let’s use a similar approach to elsewhere, so we want to be able to call `article.related_articles(:limit => 1)` (for example).
        
So, let’s add the code to our article’s test


  1234567
  def test_can_find_a_limited_result_set  article = contents(:related_article_3)  first_related_article = contents(:related_article_4)  second_related_article = contents(:article2)      assert_equal 1, article.related_articles(:limit => 1).sizeend


        
If we run the test now we’ll get an exception for not having enough parameters. So let’s add the parameter and run the test again


  12
  def related_articles(*params)  related = []


        
We’ll get a test failure (which is good – we now need to implement the restriction).


  1234567891011121314
  def related_articles(*options)  related = []  self.tags.each do |tag|    tag.articles.collect {|a| related << a if a.published && a != self }  end      if options.length == 0    related  else    params = options[0]    count = params[:limit] - 1    related[0..count]  endend


        
If we run the test again now, we’ll see it pass. It works by converting the parameters to the method into an array. We then pull down the first of the parameters (assumed to be a hash of arguments), and we then retrieve a limit from the hash.
        
Next, we want to make sure that if we specify a limit which is larger than the items we have, we return all items anyway


  1234567
  def test_retrieves_all_items_when_specifying_an_out_of_range_index  article = contents(:related_article_3)  first_related_article = contents(:related_article_4)  second_related_article = contents(:article2)     assert_equal 2, article.related_articles(:limit => 5).sizeend


Brilliant. All tests passing, we can fire up the application, add our sidebar, and enjoy the result :)

Iteration Retrospective        Well, it’s been an interesting experience. Not only because this is my first real dig around Typo’s code (which seems to be in a state of flux right now – but it’s great to see it’s getting attention!), and my first play around with Ruby code I’ve not written (which I’m sure is almost always not as great as it could be).

        
It took me a little while to figure out how to write and test the code (largely because of my unfamiliarity) with the Typo Sidebar code (and Typo in general). And the Sidebar seemed a little too complicated for my liking (I may try and refactor some of the code when I feel a little more confident in my understanding), but once again Rails’ functional testing made me so much more confident writing code.
        
What really didn’t help was not being able to find any tests for the various Sidebar components, so I had really no idea how to go about doing it! But writing code test first really made it easier for me to focus, and made me more confident the code I was writing was working. And, that should any future Typo changes be made, I know that my Sidebar is less likely to break unbeknownst to me! Piers has also commented that he is considering changing the way the Sidebars work. I’m definitely intrigued by his suggestions from what I’ve seen, and may even give some refactoring a go myself and see where I end up. You gotta love evolutionary design :)
        
I’m also not sure whether I like Rails’ definition of unit and functional testing, but I appreciate what it’s trying to achieve. I’m also still undecided as to whether I prefer having test object setup done inside fixtures versus inside the tests themselves. I had to do lots of to-ing and fro-ing between my unit test, `contents.yml`, `tags.yml` and `articles_tags.yml`. That context switching became expensive, especially during late night hotel hacking when I started to get tired and lose focus.
        
Not only that, but a lot of Typo’s tests rely on it having a predefined number of published articles (in an assumed order), by adding new articles for the new tests, it broke some of the existing tests which meant investigating that the number was indeed meant to change. I’m not too sure of the best way forward in this situation.

Where to go from here        I’m posting this article, hopefully to give people ideas about how they can go about extending existing open source Rails code, and hopefully that people will take a look at my code and make suggestions/comments about it. I’m especially excited by the prospect of people getting my code from the public Subversion repository, and submitting patches in Trac.

        
It’s this process of constantly learning from other people (whilst pairing both with other ThoughtWorkers and client developers) that makes it such a brilliant place to learn (and have fun).
        
I’m definitely going to improve the way articles are considered to be related, hopefully ordering the list based on the number of shared tags. I’m also going to set to work on writing a plugin right away to make it easy for other Typo users to get this functionality into their install without needing to update their whole application.
        
Once I’ve made a few improvements (I’d also like to make sure the persistence store access code is a little better at not having to retrieve too much data in one go) I’ll be sure to make a ticket on Typosphere and see if I can get it into the trunk for the future, if that’s where the project wants these kinds of things to go.
        
I’ll work on getting a better packaged component together as soon as possible, look out for future posts!