How to share code between a job and a controller

Viewed 482

I have some functionality in a controller that I also want in a background job (ActiveJob). If the code was all equal in both, to avoid code duplication, I guess the proper way would be to move the shared functionality to the job, and execute the job (perform_now) from the controller?

But what if the code is not all equal, where i only need some of the methods in both, what would be the proper way to tho this? Would it be to move the shared code to

  • public class methods in the job class, and call these from the controller?
  • libs? (Or are they too general?)
  • concerns?
  • helpers?

What would be the proper way to share methods between a job and a controller? How do you guys do this?

1 Answers

Both concerns and helpers are just modules. And thus can both be used for parallel inheritance.

Helpers have a specific role in Rails which is code-sharing between the view and controller, they are also automatically included into every controller which makes them a bad choice in this case. Helpers are the sweaty armpits of a Rails app and are best used for HTML generation or other view concerns.

"concerns" is really just a Rails convention which adds the folders app/controller/concerns and app/models/concerns to the auto-loading root directories. Unlike helpers they are not automatically included in your controller. If you want to use module mixins this would probably be a good place.

If the functionality is a simple set of methods this could be a good choice. There are a handful of great uses of modules like Enumerable that really provide a set of behaviors to a wide array of classes but most modules tend to be proverbial junk drawers.

Splitting things up doesn’t make things easier — it only means I can pretend that my files are small and that my main module doesn’t introduce that many methods. Even though I wrote the code, I’m constantly confused about which file holds what method. If anything, splitting out files in this way makes my job as a maintainer harder.
- Scheems

Sebastian Palma touched on one solution to this problem - Service Objects. Jobs are actually just a more advanced type of service object but you can compose a service object that encapsulates the functionality shared between the controller and job if the functionality warrants it.

public class methods in the job class, and call these from the controller?

Hell no. Jobs should just do one job and do it well. Create more jobs/service objects if needed. Don't turn a good single purpose object into a junk drawer.

Summary

What kind of objects you create matters just as much as where you place them. There is no quick and easy answer - you need to look carefully at what the code does:

  • Is it a generic behavior that can augment multiple classes? Module mixin and / or macro pattern.
  • Does it take some sort of input and do a unit of work? Service object/job.
  • Does it do HTTP calls or in some other way touch the application boundary? Client object.
  • Does it encapsulate some sort of business logic with rules that act upon it? Model/virtual model.
Related