This is a companion discussion topic for the original entry at https://thoughtbot.com/upcase/videos/invert-control
Hands down, these are the best videos on the net right now for Rubyists and possibly coders in general. Huge huge huge thanks and mad props for Ben, Joe, and the team who make these.
keep em coming, we will keep watching and implementing!
I like this kind of idea to push dependencies up into one place. In this particular example, I think we could go further by asking a question why
Sender has to know about the parser at all because what they need to know is only a collection of recipients to do their job, so I think it’s better
Sender to accept
recipients instead of
class InvitationsController < ApplicationController def create recipients = Parser.new(params[:csv_file].read).recipients Sender.new(recipients, params[:message]).send end end class Sender def initialize(recipients, message) ... end ... end
Sender has to know about
parser on how to get recipients and use each recipient object in the collection, but by doing refactoring above we reduce responsibility of
Sender to not even care how to get
recipients from the parser at all.
I absolutely agree with your assessment here. Keep the chain as short as possible without exposing the left and right link where it doesn’t need to be.
Sender probably wasn’t the best name for this class. There might exist classes whose sole purpose is to wrap the interaction with the system:
class CommentOnIssue def run(issue_id, message) comment = Comment.new(issue_id: issue_id, message: message) comment.save comment.mentioned_user_names.each do |name| user = User.find_by_name name IssueMailer.mail_mentioned_in_comment(user, comment).deliver end end end
class CommentOnIssue < Struct.new(:database, :mailer) def run(issue_id, message) comment = Comment.new(issue_id: issue_id, message: message) database.create comment comment.mentioned_user_names.each do |name| user = database.find_user_by_name name mailer.mail_mentioned_in_comment(user, comment) end end end
In the first example the abstract concept of the use case depends on the implementation details of how to persist things (using ActiveRecord) and how to mail (using ActionMailer). While the second example depends only on abstractions.
Thanks! It really means a lot to us, and you’ve just made my morning; happy friday!