Class: XlsFunction::FormatString::Evaluators::NumberEvaluator

Inherits:
Object
  • Object
show all
Defined in:
lib/xls_function/format_string/evaluators/number_evaluator.rb

Overview

TODO:

refactoring…

rubocop:disable Metrics/AbcSize

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parts) ⇒ NumberEvaluator

Returns a new instance of NumberEvaluator.



9
10
11
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 9

def initialize(parts)
  @parts = parts
end

Instance Attribute Details

#partsObject (readonly)

Returns the value of attribute parts.



7
8
9
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 7

def parts
  @parts
end

Instance Method Details

#adjust_value(value) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 88

def adjust_value(value)
  decimal_value = value.to_s.to_d

  case @unit
  when :millions
    decimal_value /= '1000000'.to_d
  when :thousands
    decimal_value /= '1000'.to_d
  end

  decimal_value *= '100'.to_d if @has_parcentage

  decimal_value
end

#call(input) ⇒ Object



13
14
15
16
17
18
19
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 13

def call(input)
  extract_parts!(input)
  value = adjust_value(input)
  format_value(value)
rescue ArgumentError
  input
end

#check_comma(comma_index) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 74

def check_comma(comma_index)
  return if comma_index.empty?

  if (comma_index_pattern_millions - comma_index).empty?
    @unit = :millions
    @format = true unless (comma_index - comma_index_pattern_millions).empty?
  elsif (comma_index_pattern_thousands - comma_index).empty?
    @unit = :thousands
    @format = true unless (comma_index - comma_index_pattern_thousands).empty?
  else
    @format = true
  end
end

#comma_index_pattern_millionsObject



69
70
71
72
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 69

def comma_index_pattern_millions
  offset = @has_parcentage ? 2 : 1
  [parts_length - offset - 1, parts_length - offset]
end

#comma_index_pattern_thousandsObject

↓ check comma When % exists, check 2 or 1 chars before from %. If not, check last 2 or 1 chars.



64
65
66
67
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 64

def comma_index_pattern_thousands
  offset = @has_parcentage ? 2 : 1
  [parts_length - offset]
end

#create_dec_pair(parts_list, value_stack) ⇒ Object



122
123
124
125
126
127
128
129
130
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 122

def create_dec_pair(parts_list, value_stack)
  parts_list.parts_with_index.map do |part, index|
    if parts_list.num?(index)
      [value_stack.shift_dec, part]
    else
      [part, nil] # means already evaluated
    end
  end
end

#create_int_pair(parts_list, value_stack) ⇒ Object



132
133
134
135
136
137
138
139
140
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 132

def create_int_pair(parts_list, value_stack)
  parts_list.parts_with_index.reverse.map do |part, index|
    if parts_list.num?(index)
      [value_stack.pop_int, part]
    else
      [part, nil]
    end
  end
end

#decimal_partsObject



25
26
27
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 25

def decimal_parts
  @decimal_parts ||= PartsList.new
end

#eval_pairs(value_eval_pairs, int_remain: false) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 142

def eval_pairs(value_eval_pairs, int_remain: false)
  require_zero = false
  value_eval_pairs.map do |value, eval|
    next value unless eval

    evaluated_value = eval.call(value)
    if evaluated_value.strip != '' && evaluated_value != '0'
      require_zero = true
      next evaluated_value
    end

    # `#` returns empty if zero but must return zero if higher place exists.
    next '0' if evaluated_value == '' && (require_zero || int_remain)

    evaluated_value
  end
end

#extract_parts!(input) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 33

def extract_parts!(input)
  parts_target = interger_parts

  comma_index = []
  @parts.each_with_index do |expr, i|
    if expr.respond_to?(:digits?)
      parts_target.add_parts(expr, i, true)
      next
    end

    # evaluate except number placeholder
    str = expr.call(input)
    # switch stack when decimal point comes
    parts_target = decimal_parts if str == '.'
    # count comma
    if str == ','
      comma_index << i
      # ignore
      next
    end
    # last % is parcentage flag
    @has_parcentage = true if str == '%' && (i == parts_length - 1)

    parts_target.add_parts(str, i, false)
  end.compact
  check_comma(comma_index)
end

#format_value(value) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 103

def format_value(value)
  value_stack = ValueStack.new(value, decimal_parts.size, @format)

  # format decimals
  decimal_chars =
    create_dec_pair(decimal_parts, value_stack).reverse
                                               .then { |value_pairs| eval_pairs(value_pairs) }
                                               .reverse
                                               .join

  # format integers
  integer_chars =
    create_int_pair(interger_parts, value_stack).reverse
                                                .then { |value_pairs| eval_pairs(value_pairs, int_remain: value_stack.int_remain?) }
                                                .join

  "#{!value_stack.empty? ? value_stack.to_s : ''}#{integer_chars}#{decimal_chars}"
end

#interger_partsObject



21
22
23
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 21

def interger_parts
  @interger_parts ||= PartsList.new
end

#parts_lengthObject



29
30
31
# File 'lib/xls_function/format_string/evaluators/number_evaluator.rb', line 29

def parts_length
  @parts_length ||= parts.length
end