ManageIQ/manageiq-consumption

View on GitHub
app/models/manageiq/showback/tier.rb

Summary

Maintainability
A
25 mins
Test Coverage
A
100%
module ManageIQ::Showback
  class Tier < ApplicationRecord
    self.table_name = 'showback_tiers'
    belongs_to :rate, :inverse_of => :tiers, :foreign_key => :showback_rate_id

    # Fixed rate costs
    # (defaults to `0`)
    # @return [Float] the subunits of a fixed rate
    monetize(:fixed_rate_subunits, :with_model_currency => :currency)
    default_value_for :fixed_rate, Money.new(0)

    # Variable rate costs
    # (defaults to `0`)
    # @return [Float] the subunits of a fixed rate
    monetize(:variable_rate_subunits, :with_model_currency => :currency)
    default_value_for :variable_rate, Money.new(0)

    # Fixed rate time apply
    # (defaults to `monthly`)
    # @return [String] a value of VALID_INTERVAL_UNITS = %w(hourly daily weekly monthly yearly)
    validates :fixed_rate_per_time, :inclusion => { :in => TimeConverterHelper::VALID_INTERVAL_UNITS }
    default_value_for :fixed_rate_per_time, 'monthly'

    # Variable rate time apply
    # (defaults to `monthly`)
    # @return [String] a value of VALID_INTERVAL_UNITS = %w(hourly daily weekly monthly yearly)
    validates :variable_rate_per_time, :inclusion => { :in => TimeConverterHelper::VALID_INTERVAL_UNITS }
    default_value_for :variable_rate_per_time, 'monthly'

    validates :variable_rate_per_unit, :presence  => true, :allow_blank => true
    validates :variable_rate_per_unit, :exclusion => { :in => [nil] }
    default_value_for :variable_rate_per_unit, ''

    # Variable tier_start_value is the start value of the tier interval
    # @return [Float]
    validates :tier_start_value, :numericality => {:greater_than_or_equal_to => 0, :less_than => Float::INFINITY}

    # Variable tier_start_value is the end value of the tier interval
    # @return [Float]
    validates :tier_end_value, :numericality => {:greater_than_or_equal_to => 0}

    validate :validate_interval

    # Get a representation string of the object
    #
    # @return [String] the definition of the object
    def name
      "#{rate.entity}:#{rate.group}:#{rate.field}:Tier:#{tier_start_value}-#{tier_end_value}"
    end

    # Validate the interval bvefore save
    #
    # @return Nothing or Error if the interval is in another tier in the same rate
    def validate_interval
      raise _("Start value of interval is greater than end value") unless tier_start_value < tier_end_value
      ManageIQ::Showback::Tier.where(:rate => rate).each do |tier|
        # Returns true == overlap, false == no overlap
        next unless self != tier
        if tier.tier_end_value == Float::INFINITY && (tier_start_value > tier.tier_start_value || tier_end_value > tier.tier_start_value)
          raise _("Interval or subinterval is in a tier with Infinity at the end")
        end
        raise _("Interval or subinterval is in another tier") if included?(tier.tier_start_value, tier.tier_end_value)
      end
    end

    # Get the range of the tier in appropiate format
    #
    # (see #set_range)
    # @return [Range] the range of the tier.
    def range
      (tier_start_value..tier_end_value) # or as an array, or however you want to return it
    end

    # Set the range of the tier
    #
    # (see #range)
    def set_range(srange, erange)
      self.tier_start_value = srange
      self.tier_end_value   = erange
      self.save
    end

    # Method to create another tier partition
    # == Parameters:
    # ::value
    #  Is the value to split the tier
    # == Returns:
    # Create the new tier from value to the end of the original tier
    #
    def divide_tier(value)
      old = tier_end_value
      self.tier_end_value = value
      self.save
      new_tier = self.dup
      new_tier.tier_end_value = old
      new_tier.tier_start_value = value
      new_tier.save
    end

    # Method to convert to float the value
    # == Parameters:
    # ::value
    #  Is the value to convert
    # == Returns:
    # Float value
    #
    def self.to_float(s)
      if s.to_s.include?("Infinity")
        Float::INFINITY
      else
        s
      end
    end

    # Check if value is inside the interval of the tier
    # == Parameters:
    # ::value
    #  Is the value to check if is inside
    # == Returns:
    # True if value is included
    # False is not
    #
    def includes?(value)
      starts_with_zero? && value.zero? || value > tier_start_value && value.to_f <= tier_end_value
    end

    # Check if the tier start in ZERO
    #
    # == Returns:
    # True if tier_start value is ZERO
    # False is not
    #
    def starts_with_zero?
      tier_start_value.zero?
    end

    # Check if the tier end in INFINITY
    #
    # == Returns:
    # True if tier_end value is INFINITY
    # False is not
    #
    def ends_with_infinity?
      tier_end_value == Float::INFINITY
    end

    # Check if the tier is free
    #
    # == Returns:
    # True if fixed_rate and variable_rate are zero
    # False is not
    #
    def free?
      fixed_rate.zero? && variable_rate.zero?
    end

    private

    # Check if the tier is included in a interval
    #
    # == Parameters:
    # start_value::
    #  Is the initial value of the interval
    # end_value::
    #  Is the end value of the interval
    # == Returns:
    # True if is included
    # False is not
    #
    def included?(start_value, end_value)
      return false if tier_end_value < start_value
      return false if tier_start_value >= end_value
      true
    end
  end
end