I have been trying to get as close to SRP in my code as possible. I have a situation where I need to log leads when interactions happen for a contact. A lead may be created or updated. There are three different states that I would like to set leads to.Active - new interested interactions causes active lead to be createdConverted - a existing lead is updated to converted if the product was purchased - this is managed with conversion flagStale - a existing lead is updated to stale if the contact is not interestedclass LeadGeneration  def initialize contact_id, options={}    @lead = Lead.find_or_initialize_by(contact_id: contact_id)    options.reverse_merge!(conversion: false, interested:true)    @conversion = options[:conversion]    @interested = options[:interested]  end  def call    if conversion?      convert_lead     else      process_lead    end  end  private   def lead    @lead  end  def conversion?    @conversion  end  def interested?    @interested  end  def convert_lead    lead.converted! unless lead.new_record?  # new leads cannot be saved as converted  end  def process_lead    if interested?      activate_lead    else      stale_lead    end  end  def activate_lead    lead.active!  end  def stale_lead    lead.stale! unless lead.new_record? # new leads cannot be saved as stale  endendRight now I feel like I should be splitting up converting a lead and managing stale/active states.  I'm not really sure what to do and tests are a little confusing right now.
Logging leads when interactions happen for a contact
ruby;ruby on rails
I'm not sure how you intend to use this class, but the call method is very fishy. This method seems to do one of three things: convert_lead, activate_lead or stale_lead. The choice of which of them should actually be executed is made in the options argument during object construction.This is not very easy to understand from reading the code, and I can't really see the reasoning behind it. Is it an implementation of a Command pattern? that is not very ruby-like.Since all this class actually does is quite basic delegation (@lead.converted!, @lead.stale! or @lead.active!) I would consider foregoing this class altogether, and simply doing the action. It is unnecessarily long and convoluted, and hides more than explains what it does.From your comment below I understand that LeadGeneration should be a Service Object. It is true that service objects may have a single method called call, as is suggested in this blog post:Service objects are responsible for retrieving and/or manipulating  data  essentially any work that needs to be done that you might have  put in a controller or model object before. They typically only have  one method, which I like to define as #call (they're usually named  something like PictureCreationService, so naming the method #create  would seem redundant).But note, though, the reason a single method call is allowable - it is apparent from the class name exactly what the method should do.If your service object was called LeadConversionService, then a call method would be good enough.Being a service object, it is even more important than usual in ruby for the class to be small and transparent.If you want to go to the extreme SRP suggested in the quote above, you'd have three classes, each with #call and #allowed? methods:class LeadConversionService  def initialize(lead)    @lead = lead  end  def call    @lead.converted!  end  def allowed?    !@lead.new_record?  endendOR, you could be a little less extreme, and decide the LeadGeneration (or maybe LeadLifeCycleService?) is a good enough scope, and let it have three method:class LeadLifeCycleService  def initialize(lead)    @lead = lead  end  def convert!    raise LifeCycleException if @lead.new_record?    @lead.convert!  end  def activate!    @lead.active!  end  def stale!    raise LifeCycleException if @lead.new_record?    @lead.stale!  endendWriting this solution makes it obvious again, that this service doesn't do much, since it only delegates (so it doesn't even save code in your model...). All it does is validates pre-conditions to your actions, which may be better handled by a state-machine, as @Flambino suggested.