Square pegs, round holes, and Rails' directory structure
Where should you put code in your Rails application? Often, the answer
is obvious: models go in app/models/
, controllers go in
app/controllers/
, and so on.
Many people have taken to extending the use of directories beneath
app/
to allow for new categories of objects, such as app/presenters/
or app/validators/
. For a little while now, Rails has embraced this
pattern – all directories beneath app/
are automatically added to
config.eager_load_paths
, so Rails will look in app/*/foo.rb
when
trying to load the Foo
class.
But what if your new object is a beautiful unique snowflake that defies categorisation?
Suppose you want to add a ChatNotifier
, which allows you to post
messages to a chat room. What category does it go in?
Perhaps, because we can say it is a “notifier”, you should create
app/notifiers/
and add it there? But you probably won’t have any other
“notifiers” in the future. Creating a whole new directory purely to
house a single file seems a tad excessive.
Hmmm… well since ChatNotifier
contains business logic, perhaps
it should go in app/models/
? That’s where business logic goes, right?
Yes! And no. Business logic is found all over your application.
There’s lots of important business logic in your models, but that
doesn’t mean you should lump everything together in one massive
directory.
I know, let’s put it in lib/
, the standard
I-don’t-know-where-the-fuck-this-goes directory! Apart from anything
else, lib/
is for code which we might share between applications,
right? We might end up sharing this!
Here’s the thing: you might
theoretically use any of your code in other applications. When you
do, I recommend you extract it to a separate codebase as a library.
Until then, this code is part of your application, and so it should live
in app/
.
lib/
is not just a junk drawer, it’s a junk drawer found in a
ramshackle outhouse, away from the rest of your stuff. Don’t use it for
application code. (Actually, maybe just don’t use it.)
In the past, I’ve created an app/misc/
directory for these situations,
and it has steadily filled up with stuff I don’t know where else to put.
This is clearly just another junk drawer, but at least it’s a junk drawer
alongside the rest of my code.
The other day, a rather obvious solution dawned on me: what if I could
just put it directly inside app/
? When working on non-Rails projects,
I have no qualms about putting things directly inside lib/{gem_name}/
.
As and when it makes sense to do so, I often group related objects and
organise them into subdirectories, but I don’t start from the assumption
that everything must be assigned a category up-front.
I’d always assumed this would be difficult to achieve with Rails’
constant autoloading
system,
but it’s not hard at all. All that is needed is the following in
config/application.rb
:
config.eager_load_paths << "#{config.root}/app"
Now when I reference ChatNotifier
, Rails will load
app/chat_notifier.rb
.
Wait, haven’t I just made app/
my new junk drawer? Maybe, but I think
it’s better than app/misc/
. Having these files at the root of app/
will encourage you to place things there sparingly, because you’ll have
to see them all the time. As and when new categories of things emerge,
you’ll organise them into new directories under app/
.
Unlike with lib/
or app/misc/
, you can’t just shut the drawer and
forget about it.
Comments
I'd love to hear from you here instead of on corporate social media platforms! You can also contact me privately.
Add your comment