One of the points I try to make in Objects on Rails is that you don’t need to let other libraries or frameworks dictate the public face of your objects. Just because you use ActiveRecord, for instance, to persist the state of an object doesn’t mean you have to commit that object to honoring the effectively infinite ActiveRecord model API. You can use the facilities of ActiveRecord internally to that object, but still project a tidy, well-understood, easily-substituted interface to the rest of the system.
And of course you can do this entirely by convention, by simply being careful to interact only with the methods you’ve explicitly exposed. But it’s easy to forget these conventions in the heat of implementation, gradually coupling the rest of the program to more and more framework-provided methods until one day you discover that your object’s interface surface area has become quite large indeed.
As an exercise in mindfully limiting the interface exposed by objects and classes, in Objects on Rails I demonstrated a tool called “
FigLeaf gives you something akin to the “private inheritance” found in some other OO languages. While it doesn’t actually hide the fact that a class is derived from a framework class, it enables you to selectively privatize some, most, or all methods derived from ancestor classes and modules.
Here’s an example from the readme
class Post < ActiveRecord::Base include FigLeaf hide ActiveRecord::Base, ancestors: true, except: [Object, :init_with, :new_record?, :errors, :valid?, :save] hide_singletons ActiveRecord::Calculations, ActiveRecord::FinderMethods, ActiveRecord::Relation # ...
And here’s what happens when we try to send class or instance-level messages which have been hidden by
ruby-1.9.2-p0 > Post.find(1) NoMethodError: private method `find' called for #<Class:0xa1a4a50> ruby-1.9.2-p0 > Post.new.destroy NoMethodError: Attempt to call private method
gem install fig_leaf
The project is also available on GitHub.
It’s important to note that
FigLeaf is not intended as a hammer to keep your coworkers or your library clients in line. It’s not as if that would work, anyway; the strictures that it adds are easy enough to circumvent. Instead, it’s intended role is more along the lines of the “rumble strips” along highways which give you a jolt when you veer off into the shoulder. It provides a sharp reminder when you’ve unthinkingly introduced a new bit of coupling to an interface you are trying to keep isolated from the rest of the codebase. Then, you can consciously make the decision whether to make that method public, or find a different way of going about what you were doing.
Got questions or suggestions about
FigLeaf or about OO practices in general? Please join us on the Objects on Rails discussion list!