Contextual Controllers

Posted by Andrew Premdas Fri, 03 Jul 2009 04:25:48 GMT

I've been struggling alot recently with controllers and with code written by my colleagues. During this period I think I've been making quite a few mistakes. One of the rules I generally like to make about things particularly in software is that if its stupid or doesn't work its probably your fault. This is a really important rule which is real easy to apply to anyone but yourself. So its really easy for me to spot others making mistakes by not applying this rule. When I hear people say Selenium doesn't work, or SASS is rubbish I can comfortably be a wise old owl and think, "ah they should just take a little more time and they will realise that these judgements are made hastily.

Another rule I like to apply is that we (human beings) are mostly ignorant about everything. Socrates (who at the time was considered the wisest of the Greeks) used to tell people seeking his wisdom that he knew nothing. This wasn't some glib bit of rhetoric to reinforce his social status. All his years of study had made him fundamentally aware of how vast knowledge is, how much depth any subject can have, and how little he really did know and understand. This ignorance goes far deeper than just a lack of factual knowledge and goes from the global to the intimate. Using this rule is really dangerous, its so easy to apply it to others and to forget to apply it to yourself, doing this results in smug ignorance. In fact the only value of this rule is self application. It is perhaps a more dangerous expression of the buddhist concept of a "begginers mind"

So what does this have to do with controllers

Well I've been struggling with existing code in our application and in particular with trying to keep code out of controllers. I don't want code in controllers for a number of reasons

  1. They seem to be much harder to spec than models
  2. It seems real easy for various bits of business logic to end up in them - where it is really hard to test
  3. Its real easy for important things like error messages to end up in controllers, and again it feels that this is the wrong place for them
  4. I'm just not comfortable with them, they don't feel like good objects.

Now I've recently been dealing with alot of existing code in controllers that I've not been happy with. In our application we are now treating addresses as a polymorphic nested entity that other entities can have. In addition we have introduced the concept of NamedAddress, which are special in the fact that they have a name and also in the way they persist. Both our controller and views; and our features and steps have struggled to isolate the addressable bit from the relationship with the particular parent.

During this process I've been learning lots, improving the code and generally working really hard on improving quality. Trouble with working really hard is that its really easy to work really stupidly and I've been doing that too. I've also been assuming that existing code is stupid. Take the following code in the index method of the OrdersController

def index
  @pending_orders = @customer.pending_orders
  @completed_orders = @customer.completed_orders
end

I broke this code (well actually the view it shows) by the following refactoring of routes

 map.resources :customers do |customers|
   customers.resources :addresses
   customers.resources :orders
 end

to

 map.resources :customers, :has_many :addresses, :shallow => true 
 map.resources :customers, :has_many :orders, :shallow => true

This is a really stupid refactoring. Why?

So back to this index method which I'm now having to look at because my features are broken. Well its obviously wrong, because its an Order controller with customer code in it. I've just spent ages making sure that my Address controller doesn't have code that refers to a specific parent, and refactored lots of features and in particular steps that should have been Addressable not Customer/Address or Order/Address. And now on top of that there are all these Order features and in particular steps that have @customer in them. So now I'm peeved and am about to start to work even harder to get this stuff sorted when ... I have to go home.

Time passes ... and I realise that "its my fault" and that "I am stupid"

Remember "if its stupid and it doesn't work its probably your fault". Remember that stupid refactoring! and the fact that your added shallow routes to the customer order relationship at the same time as to the customer address relationship. Understand that applying shallow routing will change/break numerous named routes and url generation methods. Realise how stupid it is to do this in for two relationships at the same time. Realise that doing this refactoring for one relationship should be a seperate issue and commit - and here I am trying to do this for two relationships in the middle of doing something else!! Realise that I shouldn't even be looking at the OrdersController never mind making flawed judgements about the code in it. Understand that in the 'shop' part of this application that orders fundamentally belong to a customer and cannot exist without them. Realise that my colleagues implicitly get this possibly due to their greater experience with ecommerce. Realise that if the index method in the OrdersController is flawed its only very minor (perhaps just the customer needs to made available to the view). Realise that controllers are more contextual than models; and that the implied one to one relationship between controllers and models is a figment of my misunderstanding. Realise that a simple one line before filter can express a controllers context and allow a controller to fulfill a significantly different role.

There is lots more, but I'm really tired now.

So once again programming provides that wonderful opportunity to learn, and reinforces the need for my to keep my little rules and diligently apply them to myself.

RDOC + Hanna + gem update all = pants

Posted by Andrew Premdas Fri, 29 May 2009 21:41:02 GMT

Been bitten by this multiple times. I've been using Hanna as my default rdoc template following this article. This works fine with rdoc 2.3. Unfortunately rdoc seems to be undergoing some drastic changes at the moment and rdoc 2.4 breaks this lovely stuff.

So I probably have to do something like (all with sudo)

gem update --no-rdoc --no-ri
gem uninstall rdoc # choose 2.4
gem rdoc --all --no-ri

That last command can take ages to run so this is a real gem pain at the moment

Alternative

Go with the flow and use darkfish.

Change rdoc line in .gemrc to

rdoc: --line-numbers --format=darkfish

Then

gem update --no-rdoc --no-ri     
gem rdoc --all --no-ri

Using semantic meaning in features and user interfaces

Posted by Andrew Premdas Fri, 24 Apr 2009 05:59:00 GMT

WORK IN PROGRESS

This explains why I need to fork webrat, and why I believe features should rely upon css classes and id's.

My Fork of Webrat

Posted by Andrew Premdas Fri, 24 Apr 2009 05:41:25 GMT

Webrat has become a very important part of my coding toolkit, to the point where its omissions and flaws cause great annoyance. However with the rise of Github we can just fork webrat, fix things and then issue pull requests and hope to get things back into the original.

Writing Classes why its so easy to do it wrong in Ruby

Posted by Andrew Premdas Tue, 07 Apr 2009 06:01:18 GMT

Working in Rails and dealing with Rails code and various bits of Ruby in plugins etc. it is easy to forget some OO fundamentals. This is partly due to the power of Ruby and partly due to specialist classes like controllers, models and specs distracting me away from the fact that all these things are still classes.

There used to be a rule of thumb used in Java that no method should be more than 10 lines long. Personally I took this down to 5 lines. Ruby is so powerful, that in 10 lines we can create something seriously complex, in fact we can do this in five lines. The problem with these rules of thumb is that they seem pretty stupid unless you know the underlying reasoning behind them. So lets explore this a bit further and remember how to write classes properly

Public, Private

The methods a class responds to are its public interface. This interface should document how the class works. The code that implements this interface has two purposes

  1. To further document intention, i.e. to describe what we are doing
  2. To do stuff

Surprise, surprise - the first is far more important than the second.

Learning from Java

When writing in clunkier languages like Java it becomes particularly obvious that public methods should actually delegate doing stuff to private methods. Well written java classes will have a small number of public methods, supported by a large number of private methods which actually do the work.

This is an old bit of java I wrote some time ago. The good thing about this code is that the public method clearly explains what is happening

public class Parser {

  ... // twenty lines of code defining state in private variables - including out

  public StringBuffer parse(File in) throws FileNotFoundException, IOException, ParserException {
    this.in = in;
    checkParseArgs();
    checkFileExists();
    openFile();
    initialiseStorage();
    visitedFiles.add(in);
    setCurrentWorkingDirectory();         
    while (getNextInclude()){
      processInclude();
    }
    return out;
  }

  ... // 230 lines of code and comments defining 21 void private no arg methods that actually do the work
}

Of course we don't want to go back to the verbosity of java, but in becoming much more line efficient in our code, we shouldn't forget the importance of clarity of public methods. If I turn this into English I get

Parser parses a file by,

  • checking the parse args
  • checking the file exists
  • opening the file
  • initialising the storage
  • adding the file to the visited file list
  • setting the current working directory
  • finally getting each include and processing it

Now this isn't perfect - I don't know what an include is (yet), but its fairly close to self documenting code, and its easy to find out things by just navigating to the private methods

The private methods in this class either look like the public method, or do one thing. I'll show a few of them below

private void checkParseArgs(){
    if (in == null) {
        throw new IllegalArgumentException("Parser.parse() will not except null arguments");
    }
}


private void processInclude() throws IOException, ParserException{       
    getIncludeFile();
    parseIncludeFile();
    replaceIncludeWithFile();
}  

private void getIncludeFile() throws IOException, ParserException{
    fReader.mark(tagStartInclude.length() + 255);
    readToStartOfPath();
    readPath();
    readEndTag();
}

private boolean isComment() throws IOException {
    int len = tagStartComment.length();
    char [] buf = new char[len];
    fReader.mark(len);
    fReader.read(buf,0,len);
    String s = new String(buf);
    fReader.reset(); // back to start of tag
    return tagStartComment.equals(s);
}

OK so in Ruby terms this is all very dull and long winded and we can do things much quicker and terser. But what I'd like to keep from this code is the idea that even in Ruby public class methods should clearly document their intention and in general delegate to private methods to do stuff. I'd also like to keep the idea that a method either does something or tells you how something is done - but not both. Finally I can remove all complex conditionals from my ruby code using these techniques. No need for case statements and definitely no need for nested ifs!

By the way, I wrote this parser class 7 years ago. Coming back to it, it took me about 5 minutes to work out what it does and how it does it, and this reminded me of why I wrote it. It would be nice if the ruby code I write today would be so clear in 7 years time!

Ruby Classes

With Ruby its very easy to forget about private methods and just implement 10 line public methods. From one of my Rails models I have

def weight_in_kg=(weight)
  if weight.nil? || weight == ""
    self.weight_in_grammes = weight if self.weight_in_grammes.nil?
  else
    strip = weight.strip[@@strip_regex]
    split = strip.split('.',2) unless strip.nil?
    if split.nil? || strip.nil?
      self.weight_in_grammes = weight if self.weight_in_grammes.nil?
    else
      kg = (split.first).to_i
      gms = split.size > 1 ? (split.last).to_i : 0 
      self.weight_in_grammes = kg * 1000 + gms
    end
  end
end

Now this code is just so much harder to read than the java code above. At 12 lines its not long and in Ruby its just so easy to write! And with my unit tests I can get away with this code. But I wrote this about 9 months ago - and I have less idea of what it is doing than the java code I wrote 7 years ago! Create a few hundred methods like this and you'll soon have no idea of what your code is doing.

Features for a Resource

Posted by Andrew Premdas Tue, 07 Apr 2009 04:33:32 GMT

REST is great, and resources are a great design tool. If we design our rails application around resources we can get alot of things for free. If we apply the same idea to features can we create a standard set of features to give us a quick start for each resource we identify?

Composing Features

Posted by Andrew Premdas Thu, 19 Mar 2009 02:20:24 GMT

Composing Features

Writing good features is an art-form, a very new art-form that people are just beggining to explore. Of course people have been specifying how things should work throughout history, and people have been trying to specify what software should do (with varying degrees of failure!) since software has been written.

You can compare writing features to a new school of art e.g. pontillism. Pontillism built itself on the existing foundations of painting and art - in particular colour theory - and applied these using a new technique to create something a little different. In our new school of features we produce something a little different (a plain text executable) with a new tool (Cucumber) and the application of old and new techniques. To do this well we have to learn what works and what doesn't.

Asking Questions

When we write a feature we need to ask lots of questions. A feature is an exploration of something we want to do. Starting a feature is focused on two things, loosely describing the feature and asking the most important question.

  • Why do we want to do this?

Feeds and Shit

Posted by Andrew Premdas Fri, 13 Mar 2009 01:31:02 GMT

Feeds and shit

Feeds could be a strong competitive edge for an ecommerce platform. So we should do them properly and automate the process. If we do this then we can standardise the administration functionality to

  • monitor feeds
  • deal with errors
  • deal with conflicts
  • summarize and report on changes

IPC Final

Posted by Andrew Premdas Sun, 08 Mar 2009 06:44:19 GMT

Been having some problems with my main OSX86 installation, so looking to fix these by installing IPC final

Getting started with jQuery

Posted by Andrew Premdas Thu, 22 Jan 2009 08:09:42 GMT

First thing I did wrong was

Older posts: 1 2 3 ... 9