In todays thoughtbot Boston developer discussion we discussed the merits of extracting modules vs extracting classes. Below are my notes from our lively discussion. Chime in with clarification or views of your own.
What Makes A Good Mixin?
- Repeatable, self contained functionality
- A clear contract with a minimal interface for that contract
Examples of a good mixin module are
Comparable. They both have single method contracts (
<=> respectively) and are repeatable and applicable across a wide number of objects. As a developer, you’d have no logical reason to reach into those modules and override one of the methods. Contrast this with the
ActiveModel::Model mixin. The room full of rails developers couldn’t identify what the contract for
ActiveModel::Model is. When you include AM:M, you still often find yourself reaching into it to override various methods.
** Mixins vs Object Composition in Rails **
It was agreed that mixins, as used in Rails, tend to break the rules for good mixins far more often than they follow them. Rails, unfortunately, encourages a proliferation of mixins because object composition is so difficult in, for instance ActionController or ActiveRecord.
We reviewed an example of an application that had several types of resources (
Article) that could be commented on. Each of these had their own controller that defined a single private method (
commentable). The actual functionality (
create actions) were defined in an included module.
It would be more dry (in both controllers and in your routes file) if you could use composition to do this instead, but doing so would be to fight with Rails conventions. Even if you found a solution you were happy with, you’re now outside of the typical rails convention and other Rails developers may find your code obfuscated by the new conventions you’re trying to put in place.
** Conclusion **
The conclusion we wanted to give was to not use mixins. Unfortunately, as pragmatic developers we admit that sometimes the best thing to do is to follow the conventions. Even in these situations you should consider if busting out of the rails ActionController/ActiveRecord sandbox would present an easier solution. Can you use a plain old Ruby object that could do the work just as well and offer you an opportunity to use composition instead?
Joe drew us a very helpful flow chart