Presenter or Command?
The more I think about it, the more I believe Commands and Presenters are intertwined. If you don’t know what a Presenter is, I suggest reading these articles:
- Skinny Controller, Fat Model
- Moving associated creations to the model
- Rails: Presenter Pattern
- Rails: Rise, Fall, and Potential Rebirth of the Presenter Pattern
- Simplifying your Ruby on Rails code: Presenter pattern, cells plugin
I’m building a gem to abstract the Command pattern in your applications. It’s not Rails-specific, but does know about ActiveRecord. Anyway, as I was writing my sample application, I noticed some duplication:
app/controllers/invitations_controller.rb
1 class InvitationsController < ApplicationController 2 def show 3 @user = User.invited.with_token(params[:id]).first 4 raise ActiveRecord::RecordNotFound unless @user 5 render :action => :confirm 6 end 7 end
app/commands/confirm_invitation_request_command.rb
1 class ConfirmInvitationRequestCommand 2 Komando.make_command self 3 4 mandatory_steps do 5 @user = User.invited.with_token(params[:id]).first 6 raise ActiveRecord::RecordNotFound unless @user 7 8 @user.activate!(@attributes) 9 end 10 end
Notice the first two lines of InvitationsController#show and ConfirmInvitationRequestCommand#mandatory_steps: identical. Then I thought, what if the Command was also a Presenter? Then, I could refactor appropriately:
app/commands/confirm_invitation_request_command.rb
1 class ConfirmInvitationRequestCommand 2 Komando.make_command self 3 4 def user 5 @user ||= begin 6 User.invited.with_token(params[:id]).first.tap do |user| 7 raise ActiveRecord::RecordNotFound unless user 8 end 9 end 10 end 11 12 mandatory_steps do 13 @user = User.invited.with_token(params[:id]).first 14 raise ActiveRecord::RecordNotFound unless @user 15 16 @user.activate!(@attributes) 17 end 18 end
app/controllers/invitations_controller.rb
1 class InvitationsController < ApplicationController 2 def show 3 @user = ConfirmInvitationRequestCommand.new(:token => params[:id]).user 4 render :action => :confirm 5 end 6 end
Notice how the business logic of finding a user by token is nicely tucked away.
Of course, while writing this post, I realized that finding a User by token should live in my model, not in the controller or the command, but still: bear with me.
The Presenter pattern makes some decisions for the view: how to get at the objects to be shown, how to arrange them, how to save them as a group. Turns out my Command does essentially the same thing. Maybe we can think of Presenter as a special-case of Command? Such that Presenter is a Command to view an object? That seems logical to me.