[rspec-users] Depot app, Demeter's law and troubles cleanly specing

Matt Wynne matt at mattwynne.net
Sun Apr 19 04:35:05 EDT 2009


On 18 Apr 2009, at 23:08, Lenny Marks wrote:

>
> On Apr 18, 2009, at 1:17 PM, Fernando Perez wrote:
>
>> Hi,
>>
>> Let's take the example of the depot app. In my controller I set  
>> @order.
>> Then in the view comes the bad stuff:
>>
>> @order.items.each do |item|
>> item.product.title
>> end
>>
>> Now I'm having problems specing item.product.title. A quick and dirty
>> fix is to trade a for for an underscore, so I create  
>> Item#product_title:
>>
>> def product_title
>> product.title
>> end
>>
>> The problem is that I have a few models that act the same way, so I
>> would find myself writing lot's of these little instance methods, and
>> the solution looks a bit stupid to me. So how do you handle such  
>> issue?

The risk you've identified here is that your views are coupled to the  
relationship between an order and the title of the products that make  
up that order. If you were coding down in your model layer and decided  
to make a change to the structure of your models then code far, far,  
away in the views would break. In a small application like a blog,  
this might not be a problem, but as your codebase starts to get  
bigger, it's a worry you want to avoid.

Putting on your 'outside-in' hat and thinking about *what the view  
wants*, you could code the view, which doesn't need or want to care  
about the structure of these models, more like this:

   @order.product_titles do |product_title|
     <%= product_title %>
   end

Now you're going to need to add this Order#product_titles method to  
your model layer. Having this extra method buys you more  
maintainability in the future: the structure of the model objects is  
hidden from the views, and if you need to change that structure, the  
code you'll need to change to keep the views working  
(Order#product_titles) is right down there next to you in the model  
layer where you're working. This sort of cohesion - keeping the things  
that change together in the same part of the codebase, makes  
maintenance much easier and less accident-prone.

By designing a custom protocol (think 'API') on your model layer for  
the use by the views, rather than constraining yourself to the ones  
that come out of the box with ActiveRecord, you're putting some  
lubrication in between the layers, making it easier for them to move  
independently.

The trade-off, obviously, is that you're adding extra methods to your  
models. If you imagine doing this for every view on a large project,  
you might end up with quite a bit of clutter on those models. This is  
where you could consider adding another 'facade' layer that wraps the  
models and presents them to the views in the appropriate manner. At  
Songkick, we use a presenter layer for this purpose, and it works very  
nicely.

Matt Wynne
http://blog.mattwynne.net
http://www.songkick.com



More information about the rspec-users mailing list