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
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
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)
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.
Userclass is smaller making your cognitive strain easier
Userunit 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.