You've got a few Rails applications under your belt, perhaps several. But something is wrong. As you go, your development slows and your classes become bloated and harder to understand.
Keep your program simple
While you're doing your best to follow the concept of keeping your controllers skinny and your models fat, your models are getting really fat.
The answer is simple: cut the fat.
Your models don't need to have every method they will ever need defined in the class. The reality is that the objects that your application handles only need those methods when they need them and not at any other time. Read that again if you must, because it's true.
Step 1: Evaluate your code and look for a place to separate concerns
This is the point where you look at your code and try to realize what object needs what and when.
For example, if users of your system need to approve a friend request they only need to do it in the context of viewing that request. Your User
class or @current_user
object doesn't need this ability at any other time.
Step 2: Prepare your test suite
If you want to make your code simpler and easier to understand, write tests. You must do this first.
Even if you're only intending to change one small thing (just one tiny piece), write a test for that. You need a baseline.
Step 3: Create the Object Roles
Take your friend approval (or whatever it is) method or methods and put them in a module.
You might want to drop this into some namespace such as ObjectRole::FriendApprover
or if you know your name won't clash with anything else, just go with FriendApprover
.
Here's a sample of what this might look like:
module FriendApprover
def approve(friend_request)
friend_request.approved = true
friend_request.save
increment_friends
notify_new_buddy(friend_request.user_id)
end
def increment_friends
friend_count += 1
save
end
def notify_new_buddy(buddy_id)
BuddyMailer.notify_buddy(buddy_id, "We're officially friends!")
end
end
It doesn't really matter what my sample code is, you get the picture: take the methods from your User
class that do the approval and put them in your FriendApprover
module.
The unit tests you had for these methods can now be simplified and applied to the module. The test just needs to check that some object agrees to the contract that the methods expect.
Step 4: Extend your user
Extend your user. Thats little "u" user. Your class doesn't need this module, your object does.
Open up your controller where you usually call current_user.approve(friend_request)
and change it to:
current_user.extend FriendApprover
current_user.approve(friend_request)
That's it.
What you've just done
You've made your code more obvious.
It's only in this context that a user needs to perform this action and this change has limited the scope of those methods to a very concrete area.
- Your
User
class is smaller making your cognitive strain easier - Your
User
unit test is smaller - You have a clear separation of concerns with your new Object Role module
- You've inherently made these methods reusable
But what about...
Yes, there's more to it. Of course there's more you can do, but with this simple concept you can do a lot of cleanup of both your code, and your ability to reason about your code.
What is DCI?
For now, I'll leave the description of what DCI is to this article but I'll be writing more about the concepts in Data Context and Interaction.