Search code examples
rubydryraise

How to DRY up my ruby exceptions in initialize method?


I'm writing a program in Ruby with a Product class. I have some exceptions raised whenever the Product is initialized with the wrong type of arguments. Is there a way I can DRY up my raised exceptions (am I even referring to those correctly?) I appreciate the help. Code is below:

class Product
  attr_accessor :quantity, :type, :price, :imported

  def initialize(quantity, type, price, imported)
    raise ArgumentError.new("Type must be a string") if type.class != String
    raise ArgumentError.new("Quantity must be greater than zero") if quantity <= 0
    raise ArgumentError.new("Price must be a float") if price.class != Float

    @quantity = quantity
    @type     = type
    @price    = price.round(2)
    @imported = imported
  end
end

Solution

  • The idiomatic way is to not do the type checks at all, and instead coerce the passed objects (using to_s, to_f, etc.):

    class Product
      attr_accessor :quantity, :type, :price, :imported
    
      def initialize(quantity, type, price, imported)
        raise ArgumentError.new("Quantity must be greater than zero") unless quantity > 0
    
        @quantity = quantity
        @type     = type.to_s
        @price    = price.to_f.round(2)
        @imported = imported
      end
    end
    

    You will then get the appropriate String/Float/etc. representation of the objects passed, and if they don’t know how to be coerced to those types (because they don’t respond to that method), then you’ll appropriately get a NoMethodError.

    As for the check on quantity, that looks a lot like a validation, which you may want to pull out into a separate method (especially if there gets to be a lot of them):

    class Product
      attr_accessor :quantity, :type, :price, :imported
    
      def initialize(quantity, type, price, imported)
        @quantity = quantity
        @type     = type.to_s
        @price    = price.to_f.round(2)
        @imported = imported
    
        validate!
      end
    
      private
    
      def validate!
        raise ArgumentError.new("Quantity must be greater than zero") unless @quantity > 0
      end
    end