An experiment on finding a way to create a base controller to inherit from when dealing mostly with CRUD operations.
Have you noticed that if you are working on an app, and need to create an admin interface for it, most of the work to be performed is your typical create, update, destroy and list actions?
In my “day-to-day work” in Rails-based applications, this is typically the case. I bet the same case presents to you as well, and though I am aware of sound solutions like the ActiveAdmin and RailsAdmin gems, in some edge cases the queries returned by their default implementations always leave something to be desired.
Now, let’s not confuse this with an attachment to the NIH (Not Invented Here) syndrome, but sometimes, having an in-house and flexible implementation is more than sufficient to achieve “Just the Right FeatureTM”.
Getting back to day-to-day cases in controllers, specially in a Rails application, you can easily find a pattern:
- you need to retrieve a record to display it
- you need to retrieve a record to edit/update it
- you need to delete a record
- you need to create a new record
- you need to list all existing records
Though one of Rails tenets is DRY (Don’t Repeat Yourself), you find yourself adding similar actions across multiple controller files, only changing model and routes specific details.
Let’s see an example
Suppose you’re working in a Rails app that deals with a Books and Movies catalog (among other things). You will obviously need an interface to perform CRUD operations on these resources, so you start working your way through the first controller.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
Then, comes the time to work on the next controller and you come up with this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
Then comes the time to work on another controller, and another, and another, and yet another controller… and if you pause for a moment and look at the big picture… you basically have only one controller repeated N times your models count.
How could we refactor these controllers into a flexible base use case?
Well, I recently had some time to read Growing Rails Applications in Practice and pretty much liked the suggestion on the “Beautiful controllers” chapter and decided to take it as a base to build upon… after practicing in an app I was developing at that moment, I came up with:
A small step in refactoring
Going back to our example… Let’s declare a base controller and a mixable methods module:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
|
I know, I know… this looks like an awful lot of boilerplate… but bear with me for a second while I show you how the Books and Movies controllers will look like now that we have this base class in place.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
|
If you take a look at the resulting controllers, you’ll notice we have separated the things that change (params, model, routes, etc. on each derivative controller), from those that don’t (the base CRUD methods). I bet there’s even a pattern name for this ; )
Now, suppose you need to work on building CRUD actions for a MusicAlbum controller… guess what that would look like? And the next controller you need to implement the same actions all over again?
Exactly… now that we have ourselves a base platform, we can re-use and override at will; do you need a more complicated logic in any of the actions? do you need to ensure your queries are database optimized when retrieving a collection or a single record and its associations?
Not a problem, simply override the method you need, be it the main CRUD-based action in the controller (index, show, update, etc.
), or the abstract methods provided by the base controller or the mixable module (load_resource, update_resource, etc.
) and voilá! you can have just the flexibility you need while keeping your eyes in the big picture.
While this is not a silver bullet design, I pretty much like what it provides so far. Let’s not forget that we don’t need all our controllers to inherit from this base controller; we need to evaluate the corresponding use case, but if CRUD operations is what you need repeated ad-infinitum in a uniform approach, why not take this approach for a spin?
Do you like what we’ve done here? Do you have a different opinion or approach? I’d love to know your comments on this topic. You can tweet or email me and start a conversation.