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
3user</span> = <span class="co">User</span>.invited.with_token(params[<span class="sy">:id</span>]).first <span class="no">4</span> raise <span class="co">ActiveRecord</span>::<span class="co">RecordNotFound</span> <span class="r">unless</span> <span class="iv">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
5user</span> = <span class="co">User</span>.invited.with_token(params[<span class="sy">:id</span>]).first <span class="no"> 6</span> raise <span class="co">ActiveRecord</span>::<span class="co">RecordNotFound</span> <span class="r">unless</span> <span class="iv">user
7
8user</span>.activate!(<span class="iv">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
5user</span> ||= <span class="r">begin</span> <span class="no"> 6</span> <span class="co">User</span>.invited.with_token(params[<span class="sy">:id</span>]).first.tap <span class="r">do</span> |user| <span class="no"> 7</span> raise <span class="co">ActiveRecord</span>::<span class="co">RecordNotFound</span> <span class="r">unless</span> user <span class="no"> 8</span> <span class="r">end</span> <span class="no"> 9</span> <span class="r">end</span> <span class="no"><strong>10</strong></span> <span class="r">end</span> <span class="no">11</span> <span class="no">12</span> mandatory_steps <span class="r">do</span> <span class="no">13</span> <span class="iv">user = User.invited.with_token(params[:id]).first
14 raise ActiveRecord::RecordNotFound unlessuser</span> <span class="no"><strong>15</strong></span> <span class="no">16</span> <span class="iv">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.