Calculate the percentage of sum of hash values Please review my code and help on How can I make it better calculation in method and class definition with SOLID ruby principles??

The application has a static JSON file which contains:

{
  "homes": {
    "person_a": "windows+tables",
    "person_b": "lights+tables",
    "person_c": "doors+curtains"
 }
}


The application should, for each user request, calculate quotes for the 3 insurers. The business requirement is as follows:

The quote is a 10% of the rate if 2 covers are matched or 20% if just 1 cover is matched and that is the biggest requested, 25% if it is the second biggest or 30% if is the third.

the request from user is as follow

{:curtains=&gt;20, :tables=&gt;30, :windows=&gt;50}


The system should not return a quote if the value is zero(0)

For eg: the application will return  the result for the above user request

{
    person_a: 8 (10% of 80 (two matches on windows and tables))
    person_b: 7.5 (25% of 30 (one match on tables, the 2nd biggest cover))
    insurer_c: 6 (30% of 20 (one match on curtains, the 3rd biggest cover)

}


This is my solution:


require_relative './rules'

module Coverage
  class CalculateQuotes
    def initialize(quotes)
      @quotes = quotes
    end

    def calculate_rates
      result = []
      @insurer = Coverage::Rules.parse_file ## which will give {
                                        #"insurer_rates": {
                                       #"person_a": "windows+tables",
                                       # "person_b": "lights+tables",
                                       #"person_c": "doors+curtains"
                                      # }}
      @insurer[:insurer_rates].each do |k, v|
        @match_covers = match_cover(v.split("+"))
        result &lt;&lt; [k, calculate_rate ]
      end
    end

    def match_cover(covers)
      covers = covers.map { |x| x.to_sym }
      @quotes.select { |k,v| covers.include?(k) }
    end

    def calculate_rate
      premium    = 0.0
      persentage = get_percentage_by_match_covers
      @match_covers.values.each do |v|
        premium += v * persentage
      end
      premium == 0 ? nil : premium
    end

    def get_percentage_by_match_covers
      if @match_covers.size == 2
        0.1
      elsif @match_covers.size == 1
        only_1_match_covers
      else
        0
      end
    end

    def only_1_match_covers
      index = position_of_customer_request
      case index
      when 0
        0.2
      when 1
        0.25
      when 2
        0.3
      else
        raise StandardError
      end
    end

    def position_of_customer_request
      (@quotes.to_a.reverse).index(@match_covers.to_a.flatten)
    end
  end
end


request = {:windows=&gt;50, :contents=&gt;30, :engine=&gt;20}

Coverage::CalculateQuotes.new(request).calculate_rates
````
Ruby Hash

Rather than using a verbose case statement:


 case index
      when 0
        0.2
      when 1
        0.25
      when 2
        0.3



You could use a Hash:

rates = {0 =&gt; 0.2, 1 =&gt; 0.25, 2 =&gt; 0.3}


And the case can be replaced with:

rates[index]




Rubocop Report

Use symbols as procs when possible.


covers = covers.map { |x| x.to_sym }



This is a compact alternative:

covers = covers.map(&amp;:to_sym)




Identify unused block arguments.


@quotes.select { |k,v| covers.include?(k) }



Use an underscore to identify unused block arguments:

@quotes.select { |k, _v| covers.include?(k) }




Numeric predicates should use predicate methods instead of comparison operators if possible


premium == 0 ? nil : premium



Use the predicate method zero instead:

premium.zero? ? nil : premium