I am implementing 'service objects' as per a workshop I've been studying, I'm building a reddit API application. I need the object to return something, so I can't just execute everything in the initializer. I have these two options:
Option1: Class needs instantiating
class SubListFromUser
  def user_subscribed_subs(client)
    @client = client
    @subreddits = sort_subs_by_name(user_subs_from_reddit)
  end
  private
  def sort_subs_by_name(subreddits)
    subreddits.sort_by { |sr| sr[:name].downcase }
  end 
  def user_subs_from_reddit
    @client.subscribed_subreddits :limit => 100
  end
end
Called with:
@subreddits = SubListFromUser.new(@client).user_subscribed_subs
Or Option2 is having it as a class method:
class SubListFromUser
  def self.user_subscribed_subs(client)
    sort_subs_by_name(client, user_subs_from_reddit)
  end
  private
  def self.sort_subs_by_name(subreddits)
    subreddits.sort_by { |sr| sr[:name].downcase }
  end 
  def self.user_subs_from_reddit(client)
    client.subscribed_subreddits :limit => 100
  end
end
Called with:
@subreddits = SubListFromUser.user_subscribed_subs(@client)
What is considered 'best practice' in this situation? Is there a reason I shouldn't be using object.new(args).method? I think it gives a cleaner service class but I'm not sure of the technicalities of this approach and if it has disadvantages.
Edit: Or option3 - I'm going about this all wrong and there is a better approach :)
In Ruby, I don't find that there's much of a difference.
I find the use of class variables in your "static" version a bit disturbing.
I think the class version might lead to more-creative re-use through subclassing, but that brings its own set of headaches unless things are designed as correctly as possible.
In many cases you'll need to keep a state for the process lifecycle, such as the client. Instead of having it "travel" through all methods you need it, as an argument, it makes more sense to keep it as a class variable. But for the sake of cleaner syntax, I recommend to combine the two approaches:
class SubListFromUser
  def initialize(client)
    @client = client
  end
  private_class_method :new # only this class can create instances of itself
  def user_subscribed_subs
    @subreddits = sort_subs_by_name(user_subs_from_reddit)
  end
  private
  def sort_subs_by_name(subreddits)
    subreddits.sort_by { |sr| sr[:name].downcase }
  end 
  def user_subs_from_reddit
    @client.subscribed_subreddits :limit => 100
  end
  class << self
    def user_subscribed_subs(client)
      new(client).user_subscribed_subs # create instance of this class and run a process
    end
  end
end
Call as a class method:
@subreddits = SubListFromUser.user_subscribed_subs(@client)
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With