tarakanbg/slovom

View on GitHub
lib/slovom.rb

Summary

Maintainability
B
5 hrs
Test Coverage
#encoding: utf-8
require 'slovom/version'
require 'bigdecimal'

class BigDecimal
  def slovom
    Slovom::Verbalizer.new(self).to_s
  end
end

module Slovom
  class Verbalizer

    attributes = %w[decimal levs stotinki output levs_title stotinki_title too_big]
    attributes.each { |attribute| attr_accessor attribute.to_sym }

    def initialize(decimal)
      @decimal = decimal.round(2)
      @levs = @decimal.fix.to_i
      @stotinki = ((@decimal.frac.to_s('F').gsub('0.', ''))+'0')[0..1].to_i
      @levs_title = @levs == 1 ? ' лев' : ' лева'
      @stotinki_title = @stotinki == 1 ? ' стотинка' : ' стотинки'
      @too_big = 'много'
      @output = ''
      @output += numbers_slovom(@levs) + @levs_title unless numbers_slovom(@levs).nil? || numbers_slovom(@levs) == 'много'
      @output += ' и ' unless numbers_slovom(@levs).nil? || numbers_slovom(@levs) == 'много' || numbers_slovom(@stotinki, feminine=true).nil? || numbers_slovom(@stotinki, feminine=true) == 'много'
      @output += numbers_slovom(@stotinki, feminine=true) + @stotinki_title unless numbers_slovom(@stotinki, feminine=true).nil? || numbers_slovom(@stotinki, feminine=true) == 'много'
    end

    def to_s
      @output
    end

  private

    def numbers_slovom(amount, feminine = nil)
      case amount
      when 1..99 then below_hundred(amount, feminine)
      when 100..999 then hundreds(amount, feminine)
      when 1000..999999 then thousands(amount, feminine)
      when 1000000..999999999999999 then gazillions(amount, feminine)
      else
        @too_big
      end
    end

    def below_hundred(digits, feminine = nil)
      case digits
      when 1..19 then basic_number(digits, feminine)
      when 20..99 then round_ten(digits, feminine)
      end
    end

    def basic_number(digits, feminine = nil)
      case digits
      when 1 then
        feminine == true ? 'една' : 'един'
      when 2 then
        feminine == true ? 'две' : 'два'
      when 3 then 'три'
      when 4 then 'четири'
      when 5 then 'пет'
      when 6 then 'шест'
      when 7 then 'седем'
      when 8 then 'осем'
      when 9 then 'девет'
      when 10 then 'десет'
      when 11 then 'единадесет'
      when 12 then 'дванадесет'
      when 13..19 then basic_number(digits.to_s[1].to_i)+'надесет'
      end
    end

    def round_ten(digits, feminine = nil)
      second_digit = digits.to_s.reverse.chr.to_i
      output = basic_number(digits.to_s[0].to_i)+'десет'
      output += ' и ' + basic_number(second_digit, feminine) unless second_digit == 0
      output
    end

    def hundreds(digits, feminine = nil)
      final_digits = digits.to_s[1..3].to_i
      first_digit = digits.to_s[0].to_i
      case first_digit
      when 1 then hh = 'сто'
      when 2 then hh = 'двеста'
      when 3 then hh = 'триста'
      when 4..9 then hh = basic_number(first_digit)+'стотин'
      end
      output = hh
      case final_digits
      when 1..20 then output += ' и '
      end
      output += ' ' + numbers_slovom(final_digits, feminine) unless final_digits == 0
      output.gsub(/\s+/, ' ').strip
    end

    def thousands(digits, feminine = nil)
      count = digits.to_s.length
      case count
      when 4 then
          small = digits.to_s[0].to_i
          small == 1 ? hh = 'хиляда ' : hh = numbers_slovom(small, feminine = true) + ' хиляди '
          big = digits.to_s[1..3].to_i
      when 5 then
          small = digits.to_s[0..1].to_i
          hh = numbers_slovom(small, feminine = true) + ' хиляди '
          big = digits.to_s[2..4].to_i
      when 6 then
          small = digits.to_s[0..2].to_i
          hh = numbers_slovom(small, feminine = true) + ' хиляди '
          big = digits.to_s[3..5].to_i
      end
      output = hh
      output += ' и ' unless (numbers_slovom(big).include?(' и ') && big.to_s.length == 3) or numbers_slovom(big) == 'много'
      output += numbers_slovom(big) unless big == 0
      output.gsub(/\s+/, ' ').strip
    end

    def gazillions(digits, feminine = nil)
      count = digits.to_s.length
      case count
      when 7..9 then
          base_count = 7
          word_plural = ' милиона '; word_singular = ' милион '
      when 10..12 then
          base_count = 10
          word_plural = ' милиарда '; word_singular = ' милиард '
      when 13..15 then
          base_count = 13
          word_plural = ' трилиона '; word_singular = ' трилион '
      end
      if count == base_count
        big_number = digits.to_s[0].to_i
        small_number = digits.to_s[1..24].to_i
      elsif count == base_count + 1
        big_number = digits.to_s[0..1].to_i
        small_number = digits.to_s[2..24].to_i
      elsif count == base_count + 2
        big_number = digits.to_s[0..2].to_i
        small_number = digits.to_s[3..24].to_i
      end
      word = big_number == 1 ? word_singular : word_plural

      output = numbers_slovom(big_number) + word
      output += ' и ' unless numbers_slovom(small_number).include? ' и ' or numbers_slovom(small_number) == 'много'
      output += numbers_slovom(small_number) unless numbers_slovom(small_number) == 'много'
      output.gsub(/\s+/, ' ').strip
    end

  end
end