How I organize my Rails projects

H

After rails new comes a lot of decisions. Rails has great conventions but lacks documentation and in-house libraries for services, presenters, form objects. There’s also the debate about what to do with lib. Like most people, I have my preferred ways of doing things that serve me (and my clients) well. Admittedly, a lot of the inspiration for my ways of doing things comes from GitLab. Hats off to them for having a seemingly extensible and maintainable codebase.

exploring THE APP DIRECTORY

Inside each of my project’s app directory, I create a few new directories: services, presenters, finders, and forms.

For each resource that a controller interacts with, I have a directory inside the services directory for it. Inside that resource-specific directory I have corresponding POROs for controller actions. An example would beservices/users/follow_service.rb if a route was users/:id/follow.

My presenters are most likened to that of a decorator pattern. In fact I should probably be using something like draper or cells but I’m comfortable with my POROs until I have a use case that can take advantage of the additional functionality that one of those libraries provide.

The finders directory is, admittedly, sparsely populated but important nonetheless. It contains POROs that find a resource (or collection of resources) based on specific params.

My forms directory contains form objects that act like ActiveModel and respond to the conventions that SimpleForm expects.

the lib directory

There’s some debate about what actually goes in lib, and while I’ve contributed to Rails but I wouldn’t call myself a Rails contributor.

Because we have an app directory, I would say it’s incorrect to app-specific code inside lib. But at the same time, I think it’s incorrect to throw just anything inside lib.

Some loose rules (there are many exceptions to these rules) that I think I follow:

  • if it doesn’t have routes (such as ActiveAdmin code, or an API with Grape),
  • if it doesn’t behave like ActiveModel,
  • if it doesn’t initialize with or otherwise call objects that behave like ActiveModel,
  • if it doesn’t initialize with or otherwise call ActionController::Parameters objects,

you can argue it belongs in lib.

Note: GitLab, my favorite codebase, has their grape setup within lib. … I know, I know. My rules aren’t perfect.

Inside Kioskable, my current project, I have a whopping 16 directories inside app. That’s 9 more than the default 7. admin, api, finders, inputspolicies , presenters, serializers, services, and workers. Some of these are specific to a gem: admin for ActiveAdmin, api for Grape, policies for Pundit, workers for Sidekiq.

 

By Josh