About 2 weeks ago, I wrote about a notonthehighstreet.com hackday where Roland Swingler and I started a new application with the goal of limiting its coupling with Rails. I’m planning on working on this more in the coming months, but I wanted to publish the code now in order to get feedback much earlier. So, here’s holiday: an ad hoc, informally-specified, bug-ridden, slow implementation of half of Rails.
At the moment there’s one controller, HolidayRequestsController.rb. It can display and add holiday requests. The
@holiday_request refers to a mapping object that takes input from the form and converts it into a format that can be read by the AddHolidayRequest action. I think the idea of this mapping is the most valuable thing I took from the hackday. I’ve often seen cases where it seems that developers have had to bend over backwards to deal with the vagaries of how information is collected through HTML forms. I’ve seen models with attr_accessors defined on them, because a form has an extra temporary field that’s used to make a decision on other attributes. Rails has its own complexity in this regard: look at how ActiveRecord has to handle
date_select, particularly around assignment. By separating how data needs to be represented from how it’s stored I think a lot of these issues can be minimised. In my particular example the mapping is very small. It’ll become more interesting as more fields are added: I’ll have to figure out how to cast strings to integers, for example.
AddHolidayRequest object is another interesting part of the app. I’m not sure how validations should work for my domain objects; are they inherent to the object themselves or separate? This is another area where Rails does a lot of the grunt work for you, but I wasn’t keen on including ActiveModel into my PORO domain. At the moment there’s just one validation and it happens in the action, but it feels unsustainable as an approach. Chris Flipse blogged about an interesting approach for isolating validations which I may try on my POROs.
At the moment, AddHolidayRequest is stateless. There’s no real reason for it being a class in its own right: it’s just used as a namespace with a descriptive name. Maybe I was thinking too far ahead, but I was imagining a situation where there were cross-cutting concerns for every action in the app. For instance, most of them would be undertaken by a user, so I thought it’d be nice to be able to pass that as an argument to the action itself:
AddHolidayRequest.new(current_user).call(...). This would provide a distinction between global concerns and the concerns required for the action on an individual level.
I haven’t explained all the code or decisions, but I’m really keen to hear what people think about this. I haven’t kicked the tyres enough on this yet: I think there are some interesting ideas but some things may be going too far. Since my last blog post I’ve had some interesting discussions about how other people are tackling these ideas themselves and I’d love to hear from more. I’d even love to hear from people who think I’m going about this the wrong way: show me the techniques you’re using to write maintainable Rails apps or suggest features that will completely break what I’m trying to do! So please leave a comment here or find me on twitter.