Keeping any application healthy and under control as it grows requires attention to the application’s organization and structure among other things. With DNSimple I’ve been spending some time thinking about how to reduce the size and complexity of some of the larger classes. The impetuous for this refactoring is to make the code easier to understand and easier to test. For the later the goal is to eventually be able to test business logic without needing to load the entire Rails environment.
I currently am trying two approaches to extracting logic into modules. First, I have created a directory in lib where extracted modules will live. I choose to call this directory logic since essentially I’m trying to use it as a place to store logic (as opposed to storing code that describes data models, which will remain in the app/models directory. For simpler classes that only have a small amount of logic I am extracting all of the logic into a single file with the same name as the existing model. For example, for a Contact model the logic would go into a Logic::Contact module. This module is then mixed back into the module. Here is a (somewhat contrived) example:
Before the extraction:
class Contact < ActiveRecord::Base belongs_to :user
def name "#{first_name} #{last_name}" endendAfter the extraction:
class Contact < ActiveRecord::Base include Logic::Contact belongs_to :userend
module Logic module Contact def name "#{first_name} #{last_name}" end endendThis is the simplest form of extraction. Beyond this however is another form of extraction that I am using in cases of larger, more complex classes. In this case I group together some of the logic into a module, usually naming it so it describes the behavior. For example, I have a module in DNSimple called Logic::Domain::Renewable which contains logic for domain renewal.
class Domain < ActiveRecord::Base # ...lots of code... def auto_renew? registry_domain.auto_renew? rescue => e logger.error e false end # ... more code ...endBecomes
class Domain < ActiveRecord::Base include Logic::Domain::Renewableend
module Logic module Domain module Renewable def auto_renew? registry_domain.auto_renew? rescue => e logger.error e false end end endendThe beauty of extracting in this way is that the different behaviors required by the system seem to align nicely into modules which make it much easier to locate code as I continue to develop the system.
So what might the tests look like after these extractions? I already have a fairly comprehensive test suite for DNSimple (yes, there are gaps) so it already is a little bit slow (although not as slow as it would eventually become). With the extractions I can began to test the logic in isolation. For example:
require 'lib/logic/contact'
class FakeContact include Logic::Contact attr_accessor :first_name, :last_nameend
describe Logic::Contact do subject do contact = FakeContact.new contact.first_name = "Anthony" contact.last_name = "Eden" contact end
it "has a name" do subject.name.should eq("#{subject.first_name} #{subject.last_name}") endendWhat I’m doing here is creating a contact class that is not in any way tied to ActiveRecord. This allows me to completely bypass the Rails loading process and these logic tests will run extremely fast. If you take this route you’ll need to make sure to use a modified spec helper that does not load the Rails environment. I call mine lightweight_spec_helper and only load what I need in it.
In closing, all of this is designed to make the application code easier to manage so that I can continue to improve DNSimple and make my customers happier. All of this is really only possible because I have a decent amount of test coverage, allowing me to refactor and not blow up everything in the process. Inevitably during refactorings like this things will break, but my tests cover me and protect me from myself, and thus I can extract and improve without feeling like I’ll shoot myself in the foot.

#1 by Geoffrey Grosenbach on May 7, 2011 - 10:36 am
Do you put modules in subdirectories (such as “logic/domain/renewable.rb”)?
Earlier versions of Rails sometimes had problems reloading classes in subdirectories while in development mode. But maybe it has improved or is less of an issue if things are more easily testable.
#2 by Anthony on May 7, 2011 - 10:44 am
I do put modules in subdirectories. If you simply load via a require then problems with reloading still exist, however I don’t think my specs require reloading – the only time it seems to come into play is when I run in development mode and make a change, but that’s rare, since my tests are my primary development mode.
#3 by Chris McGrath on May 7, 2011 - 11:43 am
I’ve used this approach but we’ve also moved the logic into actual classes rather than modules and that’s worth thinking about too.
We did a nice refactoring a while back that used composed_of to have rails automatically create the class we pushed the logic down into. One thing I liked about it is you end up testing the same class you actually use in the app.
For the larger bits of logic, I’ve noticed that we have ended up with groups of methods that involve the model but also some other concept in the problem domain. I think this is similar to what you’re talking about. An example from our app would be the logic that archives deleted properties. There’s no real reason it actually needs to be in the Property model. A plain ruby PropertyArchiver class that held all the logic would make more sense. There’s *lots* of scope for this kind of Extract Class refactoring in our older app.
One thing I like about classes vs modules is that it you end up with more classes responsible for just the one thing i.e. SRP. One thing to be careful of with this approach is Demeter of course
I *think* the example code you showed would be something like https://gist.github.com/960707 with the approach I’m talking about. I just typed that code into gist so it may not work / contain huge glaring errors!
I’m not saying you shouldn’t use modules, but if you have a group of methods that operate on a small portion of the data in your model you should consider if they’d be better in their own class and having the model feed them the data they need.
#4 by Jeremy Lecour on May 7, 2011 - 11:46 am
I’ve been doing exactly this for a month to share some logic components between multiple rails apps. I think it’s easier to cherry-pick with feature I need, and it stays DRY and testable.
I’d really like to discuss how to package these modules, install in apps, manage dependencies, …
#5 by Saager (@dexterous) Mhatre on May 7, 2011 - 12:38 pm
I am reminded of http://www.engineyard.com/blog/2010/let-them-code-cake/
#6 by Matt Aimonetti on May 7, 2011 - 1:52 pm
This is a very nice post, thanks Anthony. One thing I’ve been struggling with is when to extract pieces of codes and create interfaces.
After all, your modules are just good old interfaces/protocols. So for me, unless your code is shared, you shouldn’t extract it in modules. The reason why I say that is because by extracting, you add another layer of abstraction and you make your code harder to read/understand. Looking at your Contact class, I have no idea that an instance will respond to the name method. To understand the class, I will need to go down the tree and read x amount of modules to see what an instance of the class will really look like.
Something that helps me a lot is to use tools such as Yard doc to put together an alternative view of my code, making code browsing easier and offering better entry points to other developers working on stuff I wrote.
#7 by Joel Parker Henderson on May 7, 2011 - 6:49 pm
We use this approach with good success.
Like ChrisMcGrath, we lean toward creating real classes. We have used modules an an intermediate step during refactoring, and for a few cases where we do want the modules to have access to the model’s instance variables (e.g. for caching and speed).
We put the modules in the models directory, right alongside the ActiveRecord models, for example we have ./app/models/user_naming.rb
We find these various approaches to be very helpful because it organizes the code, makes it simpler to read the git change logs, and makes independent testing a breeze.
#8 by Anthony on May 7, 2011 - 10:45 pm
Thanks for the link to that post. You’re absolutely right, there are a lot of similarities in the approaches. One of the things that has been bothering me in my approach is the use of a fake class for purposes of testing. The post you’ve linked to has what appears to a better approach in its mocking (or at least something that results in less wasted code) as have some of the other comments here regarding the use of a class instead of a module. These have definitely given me food for thought.
#9 by Anthony on May 7, 2011 - 10:48 pm
Jeremy,
If something can be extracted and reused then I think it warrants breaking into a gem. I would only do that though if the code truly is reusable across more than 2 projects, and in many cases I probably wouldn’t extract it until I have actually duplicated it from one project to another and *then* find a third place, or if there are already 3 locations with similar logic that can clearly be extracted into a single shared library.
#10 by Chris McGrath on May 8, 2011 - 4:29 am
Gary Bernhardt’s destroyallsoftware.com has one on this called “Extracting Domain Objects”. FWIW I’ve just watched the rest of them last night and rate them well worth $9.