I've multiple issues to load / require classes under my app/services
folder in a Rails 5 project and I'm starting to give up on this issue.
First of all and to be clear, services/
are simple PORO classes I use throughout my project to abstract most of the business logic from the controllers, models, etc.
The tree looks like this
app/
services/
my_service/
base.rb
funny_name.rb
my_service.rb
models/
funny_name.rb
Failure #1
First, when I tried to use MyService.const_get('FunnyName')
it got FunnyName
from my models directory. It does not seem to have the same behavior when I do MyService::FunnyName
directly though, in most of my tests and changes this was working fine, it's odd.
I realised Rails config.autoload_paths
does not load things recursively ; it would makes sense that the first FunnyName
to be catch is the models/funny_name.rb
because it's definitely loaded but not the other.
That's ok, let's find a workaround. I added this to my application.rb
:
config.autoload_paths += Dir[Rails.root.join('app', 'services', '**/')]
Which will add all the subdirectories of services into config.autoload_paths
. Apparently it's not recommended to write things like that since Rails 5 ; but the idea does look right to me.
Failure #2
Now, when I start my application it crashes and output something like this
Unable to autoload constant Base, expected /.../backend/app/services/my_service/base.rb to define it (LoadError)
Names were changed but it's the matching path from the tree I wrote previously
The thing is, base.rb
is defined in the exact file the error leads me, which contains something like
class MyService
class Base
end
end
Poor solution
So I try other workaround, lots of them, nothing ever works. So I end up totally removing the autoload_paths
and add this directly in the application.rb
Dir[Rails.root.join('app', 'services', '**', '*.rb')].each { |file| require file }
Now the base.rb
is correctly loaded, the MyService.const_get('FunnyName')
will actually return the correct class and everything works, but it's a disgusting workaround. Also, it has yet not been tested in production
but it might create problems depending the environment.
Requiring the whole tree from the application.rb
sounds like a bad idea and I don't think it can be kept this way.
What's the cleanest way to add custom services/
directory in Rails ? It contains multiple subdirectories and classes with simple names which are also present in other parts of the app (models, base.rb
, etc.)
How do you avoid confusing the autoload_paths
? Is there something else I don't know which could do the trick ? Why did base.rb
even crash here ?
app/services/my_class/base.rb
start likeclass MyClass::Base
? – Allfiredclass MyClass
should bemodule MyClass
. – Allfiredmy_class.rb
at the base as well to be more correct, i'll add it – Predictory