adenaecommerce/pombo

View on GitHub
lib/pombo/package.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Pombo
  # This is the composite package of items that will be sent
  #
  # @example
  #   package = Pombo::Package.new ({
  #     destination_zip_code: '29999000',
  #     origin_zip_code: '28888000',
  #     services: "04014",
  #     in_hand: false,
  #     delivery_notice: false
  #   })
  #   # => <Pombo::Package:0x007fcfd32080f0 @items=[], @length=0, @height=0, @width=0, @declared_value=0, @destination_zip_code="29999000", @origin_zip_code="28888000">
  #
  #   package.add_item weight: 5, length: 4, height: 3, width: 5, diameter: 0
  #   package.add_item weight: 4, length: 10, height: 5, width: 5, diameter: 5, format: Pombo::Package::Format.find(:roll).code
  #   package.in_hand? # => false
  #   package.delivery_notice? # => false
  #   package.weight # => 9
  #   package.diameter # => 0
  #   package.format # => 1
  #   package.volume # => 158.17000000000002
  #   package.single_item? # => false
  class Package
    attr_accessor :destination_zip_code, :origin_zip_code, :declared_value
    attr_reader :items, :length, :height, :width, :services

    def initialize(**args)
      { items: [], length: 0, height: 0, width: 0 }.each { |key, value| instance_variable_set("@#{ key }", value) }
      args = { declared_value: 0 }.merge(args)
      args.each { |key, value| __send__("#{ key }=", value) }
    end

    # @!method in_hand?
    #   Informs you are contracted delivery in hand service
    # @!method delivery_notice?
    #   Informs you are contracted delivery notice service
    %i[in_hand delivery_notice].each do |method|
      define_method("#{ method }?"){ instance_variable_get "@#{ method }" }
    end

    %i[in_hand delivery_notice].each do |method|
      define_method("#{ method }=") do |value|
        if Pombo::Support::TRUE_VALUES.include? value
          instance_variable_set "@#{ method }", true
        elsif Pombo::Support::FALSE_VALUES.include? value
          instance_variable_set "@#{ method }", false
        else
          raise TypeError, "no implicit conversion of #{ value } into TrueClass or FalseClass"
        end
      end
    end

    # @!method length
    #   returns the length of the package or the format if `min_package` is true
    # @!method height
    #   returns the height of the package or the format if `min_package` is true
    # @!method width
    #   returns the width of the package or the format if `min_package` is true
    %i[length height width].each do |method|
      define_method("#{ method }") do
        value = instance_variable_get("@#{ method }")
        return value unless Pombo.configurations.min_package?

        current_format = Pombo::Package::Format.find(format)
        [value, current_format.send("min_#{ method }")].max
      end
    end

    # The services used to send the package
    # @return [Array<String>] codes services
    def services=(services)
      @services = services.kind_of?(Array) ? services : [services]
    end

    # It allows you to add an item to the package
    # @return [Pombo::Package::Item] the added item
    #
    # @example
    #   item = Pombo::Package::Item.new weight: 5, length: 4, height: 3, width: 5, diameter: 0
    #   package.add_item item
    #
    #   # => or
    #
    #   package.add_item weight: 5, length: 4, height: 3, width: 5, diameter: 0
    def add_item(item = nil, **args)
      item = if item.kind_of?(Pombo::Package::Item)
               item
             else
               Pombo::Package::Item.new(args)
             end

      @items << item
      update_measures
      item
    end

    # @return [Float] the total weight of the items
    def weight
      @items.inject(0) { |sum, item| sum += item.weight }
    end

    # @return [Float] the total diameter of the items
    def diameter
      return @items.first.diameter if single_item? && format == Pombo::Package::Format.find(:roll).code
      0
    end

    # @return [Fixnum] the code of the packet format
    #   For packages with more than one item format will be 1 (:box)
    def format
      return @items.first.format if single_item?
      Pombo::Package::Format.find(:box).code
    end

    # @return [Float] the total volume of the items
    def volume
      @items.inject(0) { |sum, item| sum += item.volume }
    end

    # @return [Boolean] tells if the package contains only one item
    def single_item?
      @items.size == 1 && @items.first.quantity == 1
    end

    private

    def update_measures
      if single_item?
        item = @items.first
        @length, @height, @width = item.length, item.height, item.width
      else
        @length = @height = @width = Math.cbrt volume
      end
    end
  end
end