numerals 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/lib/numerals/format/format.rb +20 -1
- data/lib/numerals/format/notations/html.rb +14 -3
- data/lib/numerals/format/notations/latex.rb +14 -3
- data/lib/numerals/format/notations/text.rb +61 -29
- data/lib/numerals/format/output.rb +0 -2
- data/lib/numerals/format/symbols.rb +117 -182
- data/lib/numerals/format/symbols/digits.rb +173 -0
- data/lib/numerals/format/symbols/padding.rb +112 -0
- data/lib/numerals/format/text_parts.rb +4 -0
- data/lib/numerals/version.rb +1 -1
- data/test/test_format.rb +47 -0
- data/test/test_format_input.rb +25 -0
- data/test/test_format_output.rb +60 -1
- metadata +4 -3
- data/lib/numerals/formatting/options.rb +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e87d9faabf0392f12d84d026d85d480aa67d32c
|
4
|
+
data.tar.gz: 67b89570a8c97ae4abe727c730bdc77099ca70d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c3b97ef87dcda6dec5dce9a8d8449da8b8da3c5faab0664ab4563dfb32f4770b9295271454c6e9c2efbec2be3b72843cf12c6f2d48395e36c7474b8d6be886c
|
7
|
+
data.tar.gz: 6ff01ddb162578b80b30596adfd38be3fb992509b8ff7aa32018b98815dae4e2449770313728647e9b8af0110e6a0cd4078edc1f4d01754a91a3205ceb7cd93a
|
data/README.md
CHANGED
@@ -161,8 +161,10 @@ Done:
|
|
161
161
|
* Handling of 'unsignificant' digits: show them either as special
|
162
162
|
symbol, as zeros or omit them (comfigured in Symbols)
|
163
163
|
|
164
|
-
Pending:
|
165
|
-
|
166
164
|
* Padding aspect of formatting on output
|
167
165
|
|
168
166
|
* Show base indicators on output
|
167
|
+
|
168
|
+
Pending:
|
169
|
+
|
170
|
+
* HTML & Latex Input/Output
|
@@ -60,6 +60,14 @@ module Numerals
|
|
60
60
|
@rounding.base
|
61
61
|
end
|
62
62
|
|
63
|
+
def padding
|
64
|
+
@symbols.padding
|
65
|
+
end
|
66
|
+
|
67
|
+
def padded?
|
68
|
+
padding.padded?
|
69
|
+
end
|
70
|
+
|
63
71
|
# Presentation base for the significand
|
64
72
|
def significand_base
|
65
73
|
base**@mode.base_scale
|
@@ -94,6 +102,7 @@ module Numerals
|
|
94
102
|
@rounding.set! places: options[:places] if options[:places]
|
95
103
|
@symbols.set! repeating: options[:repeating] if options.has_key?(:repeating)
|
96
104
|
@symbols.set! case_sensitive: options[:case_sensitive] if options.has_key?(:case_sensitive)
|
105
|
+
@symbols.set! padding: options[:padding] if options[:padding]
|
97
106
|
end
|
98
107
|
|
99
108
|
def parameters
|
@@ -130,6 +139,10 @@ module Numerals
|
|
130
139
|
set! rounding: args
|
131
140
|
end
|
132
141
|
|
142
|
+
aspect :padding do |*args|
|
143
|
+
set! padding: args
|
144
|
+
end
|
145
|
+
|
133
146
|
aspect :base do |base|
|
134
147
|
set! base: base
|
135
148
|
end
|
@@ -200,6 +213,10 @@ module Numerals
|
|
200
213
|
@symbols.set_minus!(minus)
|
201
214
|
end
|
202
215
|
|
216
|
+
aspect :leading_zeros do |width|
|
217
|
+
@symbols.set_leading_zeros! width
|
218
|
+
end
|
219
|
+
|
203
220
|
private
|
204
221
|
|
205
222
|
def extract_options(*args)
|
@@ -217,6 +234,8 @@ module Numerals
|
|
217
234
|
options[:symbols] = arg
|
218
235
|
when Symbols::Digits
|
219
236
|
options[:digits] = arg
|
237
|
+
when Symbols::Padding
|
238
|
+
options[:padding] = arg
|
220
239
|
when Format
|
221
240
|
options.merge! arg.parameters
|
222
241
|
when :exact_input
|
@@ -230,7 +249,7 @@ module Numerals
|
|
230
249
|
sci_int_digits: 1
|
231
250
|
},
|
232
251
|
symbols: {
|
233
|
-
exponent: 'p'
|
252
|
+
exponent: 'p', base_prefix: '0x'
|
234
253
|
}
|
235
254
|
)
|
236
255
|
when :gen, :general, :sci, :scientific, :fix; :fixed
|
@@ -12,20 +12,31 @@ module Numerals
|
|
12
12
|
# <span class=”numerals-num”>1.23<span class="numerals-rep">456</span> ×10<span class="numerals-sup">9</span></span>
|
13
13
|
# .numerals-rep { text-decoration: overline; }
|
14
14
|
# .numerals-sup { vertical-align: super; }
|
15
|
+
# TODO: padding
|
15
16
|
if text_parts.special?
|
16
17
|
output << escape(text_parts.special)
|
17
18
|
else
|
18
19
|
output << escape(text_parts.sign)
|
20
|
+
if format.symbols.base_prefix
|
21
|
+
output << format.symbols.base_prefix
|
22
|
+
end
|
19
23
|
output << escape(text_parts.integer) # or decide here if empty integer part is show as 0?
|
20
|
-
|
21
|
-
!text_parts.repeat? &&
|
22
|
-
!format.symbols.show_point
|
24
|
+
if text_parts.show_point?(format)
|
23
25
|
output << escape(format.symbols.point)
|
24
26
|
end
|
25
27
|
output << escape(text_parts.fractional)
|
26
28
|
if text_parts.repeat
|
27
29
|
output << %(<span style="text-decoration: overline">#{escape(text_parts.repeat)}</span>)
|
28
30
|
end
|
31
|
+
if format.symbols.base_suffix || format.base != 10
|
32
|
+
if format.symbols.base_prefix
|
33
|
+
output << format.symbols.base_suffix
|
34
|
+
else
|
35
|
+
# show base suffix as a subscript
|
36
|
+
subscript = format.symbols.base_suffix || base.to_s
|
37
|
+
º output << "<sub>#{subscript}</sub>"
|
38
|
+
end
|
39
|
+
end
|
29
40
|
if text_parts.exponent_value != 0 || format.mode.mode == :scientific
|
30
41
|
output << "×"
|
31
42
|
output << escape(text_parts.exponent_base)
|
@@ -6,20 +6,31 @@ module Numerals
|
|
6
6
|
|
7
7
|
def assemble(output, text_parts)
|
8
8
|
# 1.23\overline{456}\times10^{9}
|
9
|
+
# TODO: padding
|
9
10
|
if text_parts.special?
|
10
11
|
output << text_parts.special
|
11
12
|
else
|
12
13
|
output << text_parts.sign
|
14
|
+
if format.symbols.base_prefix
|
15
|
+
output << format.symbols.base_prefix
|
16
|
+
end
|
13
17
|
output << text_parts.integer # or decide here if empty integer part is shown as 0?
|
14
|
-
|
15
|
-
!text_parts.repeat? &&
|
16
|
-
!format.symbols.show_point
|
18
|
+
if text_parts.show_point?(format)
|
17
19
|
output << format.symbols.point
|
18
20
|
end
|
19
21
|
output << text_parts.fractional
|
20
22
|
if text_parts.repeat?
|
21
23
|
output << "\\overline{#{text_parts.repeat}}"
|
22
24
|
end
|
25
|
+
if format.symbols.base_suffix || format.base != 10
|
26
|
+
if format.symbols.base_prefix
|
27
|
+
output << format.symbols.base_suffix
|
28
|
+
else
|
29
|
+
# show base suffix as a subscript
|
30
|
+
subscript = format.symbols.base_suffix || base.to_s
|
31
|
+
output << "_{#{subscript}}"
|
32
|
+
end
|
33
|
+
end
|
23
34
|
if text_parts.exponent_value != 0 || format.mode.mode == :scientific
|
24
35
|
output << "\\times"
|
25
36
|
output << text_parts.exponent_base
|
@@ -8,35 +8,12 @@ module Numerals
|
|
8
8
|
if text_parts.special?
|
9
9
|
output << text_parts.special
|
10
10
|
else
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
!format.symbols.show_point
|
16
|
-
output << format.symbols.point
|
17
|
-
end
|
18
|
-
output << text_parts.fractional
|
19
|
-
if text_parts.repeat?
|
20
|
-
if format.symbols.repeat_delimited
|
21
|
-
output << format.symbols.repeat_begin
|
22
|
-
output << text_parts.repeat
|
23
|
-
output << format.symbols.repeat_end
|
24
|
-
else
|
25
|
-
n = RepeatDetector.min_repeat_count(
|
26
|
-
text_parts.numeral.digits.digits_array,
|
27
|
-
text_parts.numeral.repeat,
|
28
|
-
format.symbols.repeat_count - 1
|
29
|
-
)
|
30
|
-
n.times do
|
31
|
-
output << text_parts.repeat
|
32
|
-
end
|
33
|
-
output << format.symbols.repeat_suffix
|
34
|
-
end
|
35
|
-
end
|
36
|
-
if text_parts.exponent_value != 0 || format.mode.mode == :scientific
|
37
|
-
output << format.symbols.exponent
|
38
|
-
output << text_parts.exponent
|
11
|
+
if format.symbols.padding.padded?
|
12
|
+
output_size = OutputSize.new
|
13
|
+
assemble_parts(output_size, text_parts)
|
14
|
+
left_padding, internal_padding, right_padding = format.symbols.paddings(output_size.size)
|
39
15
|
end
|
16
|
+
assemble_parts(output, text_parts, left_padding, internal_padding, right_padding)
|
40
17
|
end
|
41
18
|
end
|
42
19
|
|
@@ -57,11 +34,13 @@ module Numerals
|
|
57
34
|
valid = true
|
58
35
|
base = format.significand_base
|
59
36
|
# TODO: replace numbered groups by named variables ?<var>
|
60
|
-
# TODO: ignore padding, admit base indicators
|
61
37
|
regular = /
|
62
38
|
\A
|
39
|
+
#{s.regexp(:fill, no_capture: true, optional: true, multiple: true)}
|
63
40
|
#{s.regexp(:plus, :minus)}?
|
64
41
|
\s*
|
42
|
+
#{s.regexp(:fill, no_capture: true, optional: true, multiple: true)}
|
43
|
+
#{s.regexp(:base_prefix, no_capture: true, optional: true)}
|
65
44
|
(?:
|
66
45
|
(?:(#{s.regexp(:grouped_digits, base: base, no_capture: true)}+)#{s.regexp(:point)}?)
|
67
46
|
|
|
@@ -70,7 +49,9 @@ module Numerals
|
|
70
49
|
(#{s.regexp(:digits, base: base, no_capture: true)}*)
|
71
50
|
(?:#{s.regexp(:repeat_begin)}(#{s.regexp(:digits, base: base, no_capture: true)}+)#{s.regexp(:repeat_end)})?
|
72
51
|
#{s.regexp(:repeat_suffix)}?
|
52
|
+
#{s.regexp(:base_suffix, no_capture: true, optional: true)}
|
73
53
|
(?:#{s.regexp(:exponent)}#{s.regexp(:plus, :minus)}?(\d+))?
|
54
|
+
#{s.regexp(:fill, no_capture: true, optional: true, multiple: true)}
|
74
55
|
\Z
|
75
56
|
/x
|
76
57
|
unless s.case_sensitive?
|
@@ -132,6 +113,57 @@ module Numerals
|
|
132
113
|
text_parts
|
133
114
|
end
|
134
115
|
|
116
|
+
private
|
117
|
+
|
118
|
+
class OutputSize
|
119
|
+
def initialize
|
120
|
+
@size = 0
|
121
|
+
end
|
122
|
+
def <<(text)
|
123
|
+
@size += text.size
|
124
|
+
end
|
125
|
+
attr_reader :size
|
126
|
+
end
|
127
|
+
|
128
|
+
def assemble_parts(output, text_parts, left_padding='', internal_padding='', right_padding='')
|
129
|
+
output << left_padding
|
130
|
+
output << text_parts.sign
|
131
|
+
if format.symbols.base_prefix
|
132
|
+
output << format.symbols.base_prefix
|
133
|
+
end
|
134
|
+
output << internal_padding
|
135
|
+
output << text_parts.integer # or decide here if empty integer part is show as 0?
|
136
|
+
if text_parts.show_point?(format)
|
137
|
+
output << format.symbols.point
|
138
|
+
end
|
139
|
+
output << text_parts.fractional
|
140
|
+
if text_parts.repeat?
|
141
|
+
if format.symbols.repeat_delimited
|
142
|
+
output << format.symbols.repeat_begin
|
143
|
+
output << text_parts.repeat
|
144
|
+
output << format.symbols.repeat_end
|
145
|
+
else
|
146
|
+
n = RepeatDetector.min_repeat_count(
|
147
|
+
text_parts.numeral.digits.digits_array,
|
148
|
+
text_parts.numeral.repeat,
|
149
|
+
format.symbols.repeat_count - 1
|
150
|
+
)
|
151
|
+
n.times do
|
152
|
+
output << text_parts.repeat
|
153
|
+
end
|
154
|
+
output << format.symbols.repeat_suffix
|
155
|
+
end
|
156
|
+
end
|
157
|
+
if format.symbols.base_suffix
|
158
|
+
output << format.symbols.base_suffix
|
159
|
+
end
|
160
|
+
if text_parts.exponent_value != 0 || format.mode.mode == :scientific
|
161
|
+
output << format.symbols.exponent
|
162
|
+
output << text_parts.exponent
|
163
|
+
end
|
164
|
+
output << right_padding
|
165
|
+
end
|
166
|
+
|
135
167
|
end
|
136
168
|
|
137
169
|
define_notation :text, TextNotation
|
@@ -153,8 +153,6 @@ module Numerals
|
|
153
153
|
text_parts.exponent_base = num_parts.exponent_base.to_s(10) # use digits_definition ?
|
154
154
|
text_parts.exponent_base_value = num_parts.exponent_base
|
155
155
|
end
|
156
|
-
# TODO: justification
|
157
|
-
# TODO: base indicator for significand? significand_bas?
|
158
156
|
text_parts
|
159
157
|
end
|
160
158
|
|
@@ -11,158 +11,6 @@ module Numerals
|
|
11
11
|
#
|
12
12
|
class Format::Symbols < FormattingAspect
|
13
13
|
|
14
|
-
class Digits < FormattingAspect
|
15
|
-
|
16
|
-
DEFAULT_DIGITS = %w(0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
|
17
|
-
|
18
|
-
def initialize(*args)
|
19
|
-
@digits = DEFAULT_DIGITS
|
20
|
-
@downcase_digits = @digits.map(&:downcase)
|
21
|
-
@max_base = @digits.size
|
22
|
-
@case_sensitive = false
|
23
|
-
@uppercase = false
|
24
|
-
@lowercase = false
|
25
|
-
set! *args
|
26
|
-
end
|
27
|
-
|
28
|
-
include ModalSupport::StateEquivalent
|
29
|
-
|
30
|
-
set do |*args|
|
31
|
-
options = extract_options(*args)
|
32
|
-
options.each do |option, value|
|
33
|
-
send :"#{option}=", value
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
attr_reader :digits_string, :max_base, :case_sensitive, :uppercase, :lowercase
|
38
|
-
attr_writer :case_sensitive
|
39
|
-
|
40
|
-
def digits(options = {})
|
41
|
-
base = options[:base] || @max_base
|
42
|
-
if base >= @max_base
|
43
|
-
@digits
|
44
|
-
else
|
45
|
-
@digits[0, base]
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def digits=(digits)
|
50
|
-
if digits.is_a?(String)
|
51
|
-
@digits = digits.each_char.to_a
|
52
|
-
else
|
53
|
-
@digits = digits
|
54
|
-
end
|
55
|
-
@max_base = @digits.size
|
56
|
-
@lowercase = @digits.all? { |d| d.downcase == d }
|
57
|
-
@uppercase = @digits.all? { |d| d.upcase == d }
|
58
|
-
@downcase_digits = @digits.map(&:downcase)
|
59
|
-
if @digits.uniq.size != @max_base
|
60
|
-
raise "Inconsistent digits"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def uppercase=(v)
|
65
|
-
@uppercase = v
|
66
|
-
self.digits = @digits.map(&:upcase) if v
|
67
|
-
end
|
68
|
-
|
69
|
-
def lowercase=(v)
|
70
|
-
@lowercase = v
|
71
|
-
self.digits = @digits.map(&:downcase) if v
|
72
|
-
end
|
73
|
-
|
74
|
-
def case_sensitive?
|
75
|
-
case_sensitive
|
76
|
-
end
|
77
|
-
|
78
|
-
def is_digit?(digit_symbol, options={})
|
79
|
-
base = options[:base] || @max_base
|
80
|
-
raise "Invalid base" if base > @max_base
|
81
|
-
v = digit_value(digit_symbol)
|
82
|
-
v && v < base
|
83
|
-
end
|
84
|
-
|
85
|
-
def digit_value(digit)
|
86
|
-
if @case_sensitive
|
87
|
-
@digits.index(digit)
|
88
|
-
else
|
89
|
-
@downcase_digits.index(digit.downcase)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def digit_symbol(v, options={})
|
94
|
-
base = options[:base] || @max_base
|
95
|
-
raise "Invalid base" if base > @max_base
|
96
|
-
v >= 0 && v < base ? @digits[v] : nil
|
97
|
-
end
|
98
|
-
|
99
|
-
# Convert sequence of digits to its text representation.
|
100
|
-
# The nil value can be used in the digits sequence to
|
101
|
-
# represent the group separator.
|
102
|
-
def digits_text(digit_values, options={})
|
103
|
-
insignificant_digits = options[:insignificant_digits] || 0
|
104
|
-
num_digits = digit_values.reduce(0) { |num, digit|
|
105
|
-
digit.nil? ? num : num + 1
|
106
|
-
}
|
107
|
-
num_digits -= insignificant_digits
|
108
|
-
digit_values.map { |d|
|
109
|
-
if d.nil?
|
110
|
-
options[:separator]
|
111
|
-
else
|
112
|
-
num_digits -= 1
|
113
|
-
if num_digits >= 0
|
114
|
-
digit_symbol(d, options)
|
115
|
-
else
|
116
|
-
options[:insignificant_symbol]
|
117
|
-
end
|
118
|
-
end
|
119
|
-
}.join
|
120
|
-
end
|
121
|
-
|
122
|
-
def parameters
|
123
|
-
params = {}
|
124
|
-
params[:digits] = @digits
|
125
|
-
params[:case_sensitive] = @case_sensitive
|
126
|
-
params[:uppercase] = @uppercase
|
127
|
-
params[:lowercase] = @lowercase
|
128
|
-
params
|
129
|
-
end
|
130
|
-
|
131
|
-
def to_s
|
132
|
-
# TODO: show only non-defaults
|
133
|
-
"Digits[#{parameters.inspect.unwrap('{}')}]"
|
134
|
-
end
|
135
|
-
|
136
|
-
def inspect
|
137
|
-
"Format::Symbols::#{self}"
|
138
|
-
end
|
139
|
-
|
140
|
-
def dup
|
141
|
-
Digits[parameters]
|
142
|
-
end
|
143
|
-
|
144
|
-
private
|
145
|
-
|
146
|
-
def extract_options(*args)
|
147
|
-
options = {}
|
148
|
-
args = args.first if args.size == 1 && args.first.kind_of?(Array)
|
149
|
-
args.each do |arg|
|
150
|
-
case arg
|
151
|
-
when Hash
|
152
|
-
options.merge! arg
|
153
|
-
when String, Array
|
154
|
-
options[:digits] = arg
|
155
|
-
when Format::Symbols::Digits
|
156
|
-
options.merge! arg.parameters
|
157
|
-
else
|
158
|
-
raise "Invalid Symbols::Digits definition"
|
159
|
-
end
|
160
|
-
end
|
161
|
-
options
|
162
|
-
end
|
163
|
-
|
164
|
-
end
|
165
|
-
|
166
14
|
DEFAULTS = {
|
167
15
|
nan: 'NaN',
|
168
16
|
infinity: 'Infinity',
|
@@ -175,7 +23,6 @@ module Numerals
|
|
175
23
|
repeat_begin: '<',
|
176
24
|
repeat_end: '>',
|
177
25
|
repeat_suffix: '...',
|
178
|
-
#repeat_detect: false,
|
179
26
|
show_plus: false,
|
180
27
|
show_exponent_plus: false,
|
181
28
|
uppercase: false,
|
@@ -186,7 +33,9 @@ module Numerals
|
|
186
33
|
repeat_count: 3,
|
187
34
|
grouping: [],
|
188
35
|
insignificant_digit: nil,
|
189
|
-
repeating: true
|
36
|
+
repeating: true,
|
37
|
+
base_prefix: nil,
|
38
|
+
base_suffix: nil
|
190
39
|
}
|
191
40
|
|
192
41
|
def initialize(*args)
|
@@ -199,29 +48,27 @@ module Numerals
|
|
199
48
|
# default Digits among all Symbols)
|
200
49
|
@digits = Format::Symbols::Digits[]
|
201
50
|
|
202
|
-
#
|
203
|
-
|
204
|
-
|
205
|
-
# TODO: base_suffixes, base_preffixes, show_base
|
51
|
+
# same with @padding
|
52
|
+
@padding = Format::Symbols::Padding[]
|
206
53
|
|
207
54
|
set! *args
|
208
55
|
end
|
209
56
|
|
210
|
-
# TODO: transmit uppercase/lowercase to digits
|
211
|
-
|
212
57
|
attr_reader :digits, :nan, :infinity, :plus, :minus, :exponent, :point,
|
213
|
-
:group_separator, :zero, :insignificant_digit
|
214
|
-
|
215
|
-
|
216
|
-
:show_zero, :show_point
|
217
|
-
|
218
|
-
|
219
|
-
|
58
|
+
:group_separator, :zero, :insignificant_digit, :padding,
|
59
|
+
:repeat_begin, :repeat_end, :repeat_suffix, :repeat_delimited,
|
60
|
+
:show_plus, :show_exponent_plus, :uppercase, :lowercase,
|
61
|
+
:show_zero, :show_point,
|
62
|
+
:grouping, :repeat_count, :repeating,
|
63
|
+
:base_prefix, :base_suffix
|
64
|
+
|
65
|
+
attr_writer :digits, :uppercase, :lowercase, :nan, :infinity, :plus,
|
220
66
|
:minus, :exponent, :point, :group_separator, :zero,
|
221
67
|
:repeat_begin, :repeat_end, :repeat_suffix,
|
222
68
|
:show_plus, :show_exponent_plus, :show_zero, :show_point,
|
223
69
|
:repeat_delimited, :repeat_count, :grouping,
|
224
|
-
:insignificant_digit, :repeating
|
70
|
+
:insignificant_digit, :repeating,
|
71
|
+
:base_prefix, :base_suffix
|
225
72
|
|
226
73
|
include ModalSupport::StateEquivalent
|
227
74
|
|
@@ -251,11 +98,26 @@ module Numerals
|
|
251
98
|
!@grouping.empty? && @group_separator && !@group_separator.empty?
|
252
99
|
end
|
253
100
|
|
101
|
+
def padded?
|
102
|
+
@padding.padded?
|
103
|
+
end
|
104
|
+
|
105
|
+
def fill
|
106
|
+
fill = @padding.fill
|
107
|
+
if fill.is_a?(Integer)
|
108
|
+
@digits.digit_symbol(fill)
|
109
|
+
else
|
110
|
+
fill
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
254
114
|
set do |*args|
|
255
115
|
options = extract_options(*args)
|
256
116
|
options.each do |option, value|
|
257
117
|
if option == :digits
|
258
118
|
@digits.set! value
|
119
|
+
elsif option == :padding
|
120
|
+
@padding.set! value
|
259
121
|
else
|
260
122
|
send :"#{option}=", value
|
261
123
|
end
|
@@ -263,14 +125,38 @@ module Numerals
|
|
263
125
|
apply_case!
|
264
126
|
end
|
265
127
|
|
266
|
-
attr_writer :digits, :nan, :infinity,
|
267
|
-
:plus, :minus, :exponent, :point, :group_separator, :zero,
|
268
|
-
:repeat_begin, :repeat_end, :repeat_suffix, :show_plus,
|
269
|
-
:show_exponent_plus, :uppercase, :show_zero, :show_point,
|
270
|
-
:grouping, :repeat_count
|
271
|
-
|
272
128
|
aspect :repeat do |*args|
|
273
|
-
|
129
|
+
args.each do |arg|
|
130
|
+
case arg
|
131
|
+
when true, false
|
132
|
+
@repeating = arg
|
133
|
+
when Integer
|
134
|
+
@repeat_count = arg
|
135
|
+
when :delimited
|
136
|
+
@repeat_delimited = true
|
137
|
+
when :suffixed
|
138
|
+
@repeat_delimited = false
|
139
|
+
when Hash
|
140
|
+
arg.each do |key, value|
|
141
|
+
case key
|
142
|
+
when :delimiters
|
143
|
+
@repeat_begin, @repeat_end = Array(value)
|
144
|
+
when :begin
|
145
|
+
@repeat_begin = value
|
146
|
+
when :end
|
147
|
+
@repeat_end = value
|
148
|
+
when :suffix
|
149
|
+
@repeat_suffix = value
|
150
|
+
when :delimited
|
151
|
+
@repeat_delimited = value
|
152
|
+
when :count
|
153
|
+
@repeat_count = value
|
154
|
+
else
|
155
|
+
send "#{key}=", value
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
274
160
|
end
|
275
161
|
|
276
162
|
aspect :grouping do |*args|
|
@@ -278,12 +164,14 @@ module Numerals
|
|
278
164
|
case arg
|
279
165
|
when Symbol
|
280
166
|
if arg == :thousands
|
281
|
-
@
|
167
|
+
@grouping = [3]
|
282
168
|
end
|
283
169
|
when String
|
284
170
|
@group_separator = arg
|
285
171
|
when Array
|
286
|
-
@
|
172
|
+
@grouping = arg
|
173
|
+
when false
|
174
|
+
@grouping = []
|
287
175
|
end
|
288
176
|
end
|
289
177
|
end
|
@@ -351,6 +239,14 @@ module Numerals
|
|
351
239
|
@minus = minus
|
352
240
|
end
|
353
241
|
|
242
|
+
aspect :padding do |*args|
|
243
|
+
@padding.set! *args
|
244
|
+
end
|
245
|
+
|
246
|
+
aspect :leading_zeros do |width|
|
247
|
+
@padding.leading_zeros = width
|
248
|
+
end
|
249
|
+
|
354
250
|
def parameters(abbreviated=false)
|
355
251
|
params = {}
|
356
252
|
DEFAULTS.each do |param, default|
|
@@ -362,6 +258,9 @@ module Numerals
|
|
362
258
|
if !abbreviated || @digits != Format::Symbols::Digits[]
|
363
259
|
params[:digits] = @digits
|
364
260
|
end
|
261
|
+
if !abbreviated || @padding != Format::Symbols::Padding[]
|
262
|
+
params[:padding] = @padding
|
263
|
+
end
|
365
264
|
params
|
366
265
|
end
|
367
266
|
|
@@ -447,7 +346,9 @@ module Numerals
|
|
447
346
|
symbols = args
|
448
347
|
digits = symbols.delete(:digits)
|
449
348
|
grouped_digits = symbols.delete(:grouped_digits)
|
450
|
-
symbols = symbols.map { |s|
|
349
|
+
symbols = symbols.map { |s|
|
350
|
+
s.is_a?(Symbol) ? send(s) : s
|
351
|
+
}
|
451
352
|
if grouped_digits
|
452
353
|
symbols += [group_separator, insignificant_digit]
|
453
354
|
elsif digits
|
@@ -479,6 +380,23 @@ module Numerals
|
|
479
380
|
}.compact
|
480
381
|
end
|
481
382
|
|
383
|
+
# Returns left, internal and right padding for a number
|
384
|
+
# of given size (number of characters)
|
385
|
+
def paddings(number_size)
|
386
|
+
left_padding = internal_padding = right_padding = ''
|
387
|
+
if padded?
|
388
|
+
left_padding_size, internal_padding_size, right_padding_size = padding.padding_sizes(number_size)
|
389
|
+
right_padding_size = right_padding_size/fill.size
|
390
|
+
right_padding = fill*right_padding_size
|
391
|
+
d = right_padding_size - right_padding.size
|
392
|
+
left_padding_size = (left_padding_size + d)/fill.size
|
393
|
+
left_padding = fill*left_padding_size
|
394
|
+
internal_padding_size = internal_padding_size/fill.size
|
395
|
+
internal_padding = fill*internal_padding_size
|
396
|
+
end
|
397
|
+
[left_padding, internal_padding, right_padding ]
|
398
|
+
end
|
399
|
+
|
482
400
|
private
|
483
401
|
|
484
402
|
def regexp_char(c, options = {})
|
@@ -500,10 +418,22 @@ module Numerals
|
|
500
418
|
symbols = Array(symbols).compact.select { |s| !s.empty? }
|
501
419
|
.map{ |d| regexp_symbol(d, options) }.join('|')
|
502
420
|
if capture
|
503
|
-
"(#{symbols})"
|
421
|
+
symbols = "(#{symbols})"
|
504
422
|
else
|
505
|
-
|
423
|
+
if symbols != ''
|
424
|
+
symbols = "(?:#{symbols})"
|
425
|
+
if options[:optional]
|
426
|
+
if options[:multiple]
|
427
|
+
symbols = "#{symbols}*"
|
428
|
+
else
|
429
|
+
symbols = "#{symbols}?"
|
430
|
+
end
|
431
|
+
elsif options[:multiple]
|
432
|
+
symbols = "#{symbols}+"
|
433
|
+
end
|
434
|
+
end
|
506
435
|
end
|
436
|
+
symbols
|
507
437
|
end
|
508
438
|
|
509
439
|
def extract_options(*args)
|
@@ -540,7 +470,8 @@ module Numerals
|
|
540
470
|
@repeat_begin = @repeat_begin.upcase
|
541
471
|
@repeat_end = @repeat_end.upcase
|
542
472
|
@repeat_suffix = @repeat_suffix.upcase
|
543
|
-
@digits
|
473
|
+
@digits.set! uppercase: true
|
474
|
+
@padding.fill = @padding.fill.upcase if @padding.fill.is_a?(String)
|
544
475
|
elsif @lowercase
|
545
476
|
@nan = @nan.downcase
|
546
477
|
@infinity = @infinity.downcase
|
@@ -552,7 +483,8 @@ module Numerals
|
|
552
483
|
@repeat_begin = @repeat_begin.downcase
|
553
484
|
@repeat_end = @repeat_end.downcase
|
554
485
|
@repeat_suffix = @repeat_suffix.downcase
|
555
|
-
@digits
|
486
|
+
@digits.set! lowercase: true
|
487
|
+
@padding.fill = @padding.fill.downcase if @padding.filll.is_a?(String)
|
556
488
|
end
|
557
489
|
end
|
558
490
|
|
@@ -563,3 +495,6 @@ module Numerals
|
|
563
495
|
end
|
564
496
|
|
565
497
|
end
|
498
|
+
|
499
|
+
require 'numerals/format/symbols/digits'
|
500
|
+
require 'numerals/format/symbols/padding'
|
@@ -0,0 +1,173 @@
|
|
1
|
+
module Numerals
|
2
|
+
|
3
|
+
class Format::Symbols::Digits < FormattingAspect
|
4
|
+
|
5
|
+
DEFAULT_DIGITS = %w(0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
@digits = DEFAULT_DIGITS
|
9
|
+
@downcase_digits = @digits.map(&:downcase)
|
10
|
+
@max_base = @digits.size
|
11
|
+
@case_sensitive = false
|
12
|
+
@uppercase = false
|
13
|
+
@lowercase = false
|
14
|
+
set! *args
|
15
|
+
end
|
16
|
+
|
17
|
+
include ModalSupport::StateEquivalent
|
18
|
+
|
19
|
+
set do |*args|
|
20
|
+
options = extract_options(*args)
|
21
|
+
options.each do |option, value|
|
22
|
+
send :"#{option}=", value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :digits_string, :max_base, :case_sensitive, :uppercase, :lowercase
|
27
|
+
attr_writer :case_sensitive
|
28
|
+
|
29
|
+
def digits(options = {})
|
30
|
+
base = options[:base] || @max_base
|
31
|
+
if base >= @max_base
|
32
|
+
@digits
|
33
|
+
else
|
34
|
+
@digits[0, base]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def digits=(digits)
|
39
|
+
if digits.is_a?(String)
|
40
|
+
@digits = digits.each_char.to_a
|
41
|
+
else
|
42
|
+
@digits = digits
|
43
|
+
end
|
44
|
+
@max_base = @digits.size
|
45
|
+
@lowercase = @digits.all? { |d| d.downcase == d }
|
46
|
+
@uppercase = @digits.all? { |d| d.upcase == d }
|
47
|
+
@downcase_digits = @digits.map(&:downcase)
|
48
|
+
if @digits.uniq.size != @max_base
|
49
|
+
raise "Inconsistent digits"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def uppercase=(v)
|
54
|
+
@uppercase = v
|
55
|
+
self.digits = @digits.map(&:upcase) if v
|
56
|
+
end
|
57
|
+
|
58
|
+
def lowercase=(v)
|
59
|
+
@lowercase = v
|
60
|
+
self.digits = @digits.map(&:downcase) if v
|
61
|
+
end
|
62
|
+
|
63
|
+
def case_sensitive?
|
64
|
+
case_sensitive
|
65
|
+
end
|
66
|
+
|
67
|
+
def is_digit?(digit_symbol, options={})
|
68
|
+
base = options[:base] || @max_base
|
69
|
+
raise "Invalid base" if base > @max_base
|
70
|
+
v = digit_value(digit_symbol)
|
71
|
+
v && v < base
|
72
|
+
end
|
73
|
+
|
74
|
+
def digit_value(digit)
|
75
|
+
if @case_sensitive
|
76
|
+
@digits.index(digit)
|
77
|
+
else
|
78
|
+
@downcase_digits.index(digit.downcase)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def digit_symbol(v, options={})
|
83
|
+
base = options[:base] || @max_base
|
84
|
+
raise "Invalid base" if base > @max_base
|
85
|
+
v >= 0 && v < base ? @digits[v] : nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# Convert sequence of digits to its text representation.
|
89
|
+
# The nil value can be used in the digits sequence to
|
90
|
+
# represent the group separator.
|
91
|
+
def digits_text(digit_values, options={})
|
92
|
+
insignificant_digits = options[:insignificant_digits] || 0
|
93
|
+
num_digits = digit_values.reduce(0) { |num, digit|
|
94
|
+
digit.nil? ? num : num + 1
|
95
|
+
}
|
96
|
+
num_digits -= insignificant_digits
|
97
|
+
digit_values.map { |d|
|
98
|
+
if d.nil?
|
99
|
+
options[:separator]
|
100
|
+
else
|
101
|
+
num_digits -= 1
|
102
|
+
if num_digits >= 0
|
103
|
+
digit_symbol(d, options)
|
104
|
+
else
|
105
|
+
options[:insignificant_symbol]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
}.join
|
109
|
+
end
|
110
|
+
|
111
|
+
def parameters
|
112
|
+
params = {}
|
113
|
+
params[:digits] = @digits
|
114
|
+
params[:case_sensitive] = @case_sensitive
|
115
|
+
params[:uppercase] = @uppercase
|
116
|
+
params[:lowercase] = @lowercase
|
117
|
+
params
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_s
|
121
|
+
# TODO: show only non-defaults
|
122
|
+
args = []
|
123
|
+
if @digits != DEFAULT_DIGITS
|
124
|
+
args << @digits.to_s
|
125
|
+
end
|
126
|
+
if @max_base != @digits.size
|
127
|
+
args << "max_base: #{@max_base}"
|
128
|
+
end
|
129
|
+
if @case_sensitive
|
130
|
+
args << "case_sensitive: #{case_sensitive.inspect}"
|
131
|
+
end
|
132
|
+
if @uppercase
|
133
|
+
args << "uppercase: #{uppercase.inspect}"
|
134
|
+
end
|
135
|
+
if @lowercase
|
136
|
+
args << "lowercase: #{lowercase.inspect}"
|
137
|
+
end
|
138
|
+
"Digits[#{args.join(', ')}]"
|
139
|
+
end
|
140
|
+
|
141
|
+
def inspect
|
142
|
+
"Format::Symbols::#{self}"
|
143
|
+
end
|
144
|
+
|
145
|
+
def dup
|
146
|
+
Format::Symbols::Digits[parameters]
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def extract_options(*args)
|
152
|
+
options = {}
|
153
|
+
args = args.first if args.size == 1 && args.first.kind_of?(Array)
|
154
|
+
args.each do |arg|
|
155
|
+
case arg
|
156
|
+
when Hash
|
157
|
+
options.merge! arg
|
158
|
+
when String, Array
|
159
|
+
options[:digits] = arg
|
160
|
+
when Format::Symbols::Digits
|
161
|
+
options.merge! arg.parameters
|
162
|
+
when :uppercase, :downcase
|
163
|
+
send :"#{arg}=", true
|
164
|
+
else
|
165
|
+
raise "Invalid Symbols::Digits definition"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
options
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Numerals
|
2
|
+
|
3
|
+
# Padding a number to a given width
|
4
|
+
class Format::Symbols::Padding < FormattingAspect
|
5
|
+
|
6
|
+
# Parameters:
|
7
|
+
#
|
8
|
+
# * :width field width (0 for no padding)
|
9
|
+
# * :fill filling symbol; nil for no padding;
|
10
|
+
# 0 to use the digit zero; otherwise should be a String
|
11
|
+
# * :adjust adjust mode: :left, :right, :integer, :center
|
12
|
+
#
|
13
|
+
def initialize(*args)
|
14
|
+
@width = 0
|
15
|
+
@fill = nil
|
16
|
+
@adjust = :right
|
17
|
+
set! *args
|
18
|
+
end
|
19
|
+
|
20
|
+
include ModalSupport::StateEquivalent
|
21
|
+
|
22
|
+
set do |*args|
|
23
|
+
options = extract_options(*args)
|
24
|
+
options.each do |option, value|
|
25
|
+
send :"#{option}=", value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_accessor :width, :fill, :adjust
|
30
|
+
|
31
|
+
def leading_zeros=(width)
|
32
|
+
@width = width
|
33
|
+
@fill = 0
|
34
|
+
@adjust = :internal
|
35
|
+
end
|
36
|
+
|
37
|
+
def padded?
|
38
|
+
@width > 0 && @fill && @fill != ''
|
39
|
+
end
|
40
|
+
|
41
|
+
def parameters
|
42
|
+
{ width: width, fill: fill, adjust: adjust }
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
params = []
|
47
|
+
if fill == 0 && adjust == :internal
|
48
|
+
params << "leading_zeros: #{width}"
|
49
|
+
else
|
50
|
+
if width != 0
|
51
|
+
params << "width: #{width}"
|
52
|
+
end
|
53
|
+
if fill
|
54
|
+
params << "fill: #{fill.inspect}"
|
55
|
+
end
|
56
|
+
if adjust != :right || !params.empty?
|
57
|
+
params << "adjust: #{adjust.inspect}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
"Padding[#{params.join(', ')}]"
|
61
|
+
end
|
62
|
+
|
63
|
+
def inspect
|
64
|
+
"Format::Symbols::#{to_s}"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns size (characters of left, internal and right padding)
|
68
|
+
# for a number of given width (without padding)
|
69
|
+
def padding_sizes(number_size)
|
70
|
+
left_padding_size = internal_padding_size = right_padding_size = 0
|
71
|
+
padding_size = width - number_size
|
72
|
+
if padding_size > 0 && padded?
|
73
|
+
case adjust
|
74
|
+
when :left
|
75
|
+
left_padding_size = padding_size
|
76
|
+
when :right
|
77
|
+
right_padding_size = padding_size
|
78
|
+
when :internal
|
79
|
+
internal_padding_size = padding_size
|
80
|
+
when :center
|
81
|
+
left_padding_size = (padding_size + 1) / 2
|
82
|
+
right_padding_size = padding_size - left_padding_size
|
83
|
+
end
|
84
|
+
end
|
85
|
+
[left_padding_size, internal_padding_size, right_padding_size]
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def extract_options(*args)
|
91
|
+
options = {}
|
92
|
+
args = args.first if args.size == 1 && args.first.kind_of?(Array)
|
93
|
+
args.each do |arg|
|
94
|
+
case arg
|
95
|
+
when Integer
|
96
|
+
options.merge! width: arg
|
97
|
+
when String
|
98
|
+
options.merge! fill: arg
|
99
|
+
when :left, :right, :internal, :center
|
100
|
+
options.merge! adjust: arg
|
101
|
+
when Hash
|
102
|
+
options.merge! arg
|
103
|
+
when Format::Symbols::Padding
|
104
|
+
options.merge! arg.parameters
|
105
|
+
end
|
106
|
+
end
|
107
|
+
options
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
data/lib/numerals/version.rb
CHANGED
data/test/test_format.rb
CHANGED
@@ -121,4 +121,51 @@ class TestFormat < Test::Unit::TestCase # < Minitest::Test
|
|
121
121
|
assert_equal 'PLUS', f2.symbols.plus
|
122
122
|
end
|
123
123
|
|
124
|
+
def test_repeat_aspect
|
125
|
+
s = Format::Symbols[]
|
126
|
+
assert_equal '<', s.repeat_begin
|
127
|
+
assert_equal '>', s.repeat_end
|
128
|
+
assert_equal '...', s.repeat_suffix
|
129
|
+
refute s.repeat_delimited
|
130
|
+
assert_equal 3, s.repeat_count
|
131
|
+
assert s.repeating
|
132
|
+
|
133
|
+
s.set_repeat! false
|
134
|
+
refute s.repeating
|
135
|
+
|
136
|
+
s.set_repeat! true, delimiters: '[', count: 2, suffix: '****'
|
137
|
+
assert_equal '[', s.repeat_begin
|
138
|
+
assert_nil s.repeat_end
|
139
|
+
assert_equal '****', s.repeat_suffix
|
140
|
+
refute s.repeat_delimited
|
141
|
+
assert_equal 2, s.repeat_count
|
142
|
+
assert s.repeating
|
143
|
+
|
144
|
+
s.set_repeat! true, delimiters: ['>', '<'], delimited: true
|
145
|
+
assert_equal '>', s.repeat_begin
|
146
|
+
assert_equal '<', s.repeat_end
|
147
|
+
assert s.repeat_delimited
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_padding_aspect
|
151
|
+
f = Format[]
|
152
|
+
refute f.padded?
|
153
|
+
assert_equal :right, f.padding.adjust
|
154
|
+
f = f[padding:[10, ' ', :left]]
|
155
|
+
assert f.padded?
|
156
|
+
assert_equal :left, f.padding.adjust
|
157
|
+
assert_equal ' ', f.padding.fill
|
158
|
+
end
|
159
|
+
|
160
|
+
def tst_grouping
|
161
|
+
f = Format[]
|
162
|
+
refute f.grouping?
|
163
|
+
f.set_grouping! :thousands
|
164
|
+
assert f.grouping?
|
165
|
+
assert_equal [3], f.grouping
|
166
|
+
f.set_grouping! false
|
167
|
+
refute f.grouping?
|
168
|
+
assert_equal [], f.grouping
|
169
|
+
end
|
170
|
+
|
124
171
|
end
|
data/test/test_format_input.rb
CHANGED
@@ -223,4 +223,29 @@ class TestFormatInput < Test::Unit::TestCase # < Minitest::Test
|
|
223
223
|
|
224
224
|
end
|
225
225
|
|
226
|
+
def test_padding
|
227
|
+
f = Format[padding: '*']
|
228
|
+
assert_equal 643454333.32, f.read("******643,454,333.32", type: Float)
|
229
|
+
assert_equal -643454333.32, f.read("*****-643,454,333.32", type: Float)
|
230
|
+
assert_equal -643454333.32, f.read("-*****643,454,333.32", type: Float)
|
231
|
+
assert_equal 643454333.32, f.read("+*****643,454,333.32", type: Float)
|
232
|
+
assert_equal 643454333.32, f.read("643,454,333.32******", type: Float)
|
233
|
+
assert_equal -643454333.32, f.read("-643,454,333.32*****", type: Float)
|
234
|
+
assert_equal 643454333.32, f.read("***643,454,333.32***", type: Float)
|
235
|
+
assert_equal 643454333.32, f.read("***643,454,333.32***", type: Float)
|
236
|
+
f.set_leading_zeros! 10
|
237
|
+
assert_equal 123, f.read("0000000123", type: Integer)
|
238
|
+
assert_equal -123, f.read("-000000123", type: Integer)
|
239
|
+
assert_equal 123.5, f.read("00000123.5", type: Float)
|
240
|
+
assert_equal -123.5, f.read("-00000123.5000", type: Float)
|
241
|
+
assert_equal 123.5, f.read("00000123.5000", type: Float)
|
242
|
+
assert_equal 100.5, f.read("00000100.5", type: Float)
|
243
|
+
assert_equal -100.5, f.read("-0000100.5", type: Float)
|
244
|
+
assert_equal Rational(1,3), f.read("000000.<3>", type: Rational)
|
245
|
+
assert_equal Rational(-1,3), f.read("-00000.<3>", type: Rational)
|
246
|
+
f.set_padding! '*'
|
247
|
+
assert_equal Flt::DecNum('0.667'), f.read("********0.667*******", type: Flt::DecNum)
|
248
|
+
assert_equal Flt::DecNum('-0.667'), f.read("*******-0.667*******", type: Flt::DecNum)
|
249
|
+
end
|
250
|
+
|
226
251
|
end
|
data/test/test_format_output.rb
CHANGED
@@ -229,7 +229,7 @@ class TestFormatOutput < Test::Unit::TestCase # < Minitest::Test
|
|
229
229
|
def test_write_binnum_hex
|
230
230
|
context = Flt::BinNum::IEEEDoubleContext
|
231
231
|
x = Flt::BinNum('0.1', :fixed, context: context)
|
232
|
-
assert_equal "
|
232
|
+
assert_equal "0x1.999999999999Ap-4", Format[:hexbin].write(x)
|
233
233
|
end
|
234
234
|
|
235
235
|
def test_write_to_file
|
@@ -786,4 +786,63 @@ class TestFormatOutput < Test::Unit::TestCase # < Minitest::Test
|
|
786
786
|
Format[:short, repeating: false].write(Rational(2469,200))
|
787
787
|
end
|
788
788
|
end
|
789
|
+
|
790
|
+
def test_padding
|
791
|
+
f = Format[padding: [:right, 20, fill: 0]]
|
792
|
+
|
793
|
+
f = Format[padding: [:left, 20, fill: '*']]
|
794
|
+
f = f[symbols: [group_separator: ',', grouping: [3]]]
|
795
|
+
assert_equal "******643,454,333.32", f.write(643454333.32)
|
796
|
+
assert_equal "*****-643,454,333.32", f.write(-643454333.32)
|
797
|
+
assert_equal "-*****643,454,333.32", f[padding: :internal].write(-643454333.32)
|
798
|
+
f = f[padding: :right]
|
799
|
+
assert_equal "643,454,333.32******", f.write(643454333.32)
|
800
|
+
assert_equal "-643,454,333.32*****", f.write(-643454333.32)
|
801
|
+
f = f[padding: :center]
|
802
|
+
assert_equal "***643,454,333.32***", f.write(643454333.32)
|
803
|
+
assert_equal "***-643,454,333.32**", f.write(-643454333.32)
|
804
|
+
f.set_leading_zeros! 10
|
805
|
+
assert_equal "0000000123", f.write(123)
|
806
|
+
assert_equal "-000000123", f.write(-123)
|
807
|
+
assert_equal "00000123.5", f.write(123.5)
|
808
|
+
assert_equal "-0000123.5", f.write(-123.5)
|
809
|
+
f.set_grouping! false
|
810
|
+
assert_equal "0123456789", f.write(123456789)
|
811
|
+
assert_equal "1234567891", f.write(1234567891)
|
812
|
+
assert_equal "12345678912", f.write(12345678912)
|
813
|
+
assert_equal "-123456789", f.write(-123456789)
|
814
|
+
assert_equal "-1234567891", f.write(-1234567891)
|
815
|
+
assert_equal "-12345678912", f.write(-12345678912)
|
816
|
+
f.set! symbols: [repeat_delimited: true]
|
817
|
+
assert_equal "000000.<3>", f.write(Rational(1,3))
|
818
|
+
assert_equal "-00000.<3>", f.write(Rational(-1,3))
|
819
|
+
assert_equal "000000.<6>", f.write(Rational(2,3))
|
820
|
+
assert_equal "-00000.<6>", f.write(Rational(-2,3))
|
821
|
+
f.set! rounding: [places: 3]
|
822
|
+
assert_equal "000000.333", f.write(Rational(1,3))
|
823
|
+
assert_equal "-00000.333", f.write(Rational(-1,3))
|
824
|
+
assert_equal "000000.667", f.write(Rational(2,3))
|
825
|
+
assert_equal "-00000.667", f.write(Rational(-2,3))
|
826
|
+
f.set! rounding: [places: 4]
|
827
|
+
assert_equal "00000.3333", f.write(Rational(1,3))
|
828
|
+
assert_equal "-0000.3333", f.write(Rational(-1,3))
|
829
|
+
assert_equal "00000.6667", f.write(Rational(2,3))
|
830
|
+
assert_equal "-0000.6667", f.write(Rational(-2,3))
|
831
|
+
f.set! padding: [:center, width: 20, fill: '*']
|
832
|
+
f.set! rounding: [places: 3]
|
833
|
+
assert_equal "********0.667*******", f.write(Rational(2,3))
|
834
|
+
assert_equal "*******-0.667*******", f.write(Rational(-2,3))
|
835
|
+
f.set! rounding: [places: 4]
|
836
|
+
assert_equal "*******0.5555*******", f.write(Flt::DecNum('0.5555'))
|
837
|
+
f.set! padding: 8
|
838
|
+
assert_equal "*0.5555*", f.write(Flt::DecNum('0.5555'))
|
839
|
+
f.set! padding: 7
|
840
|
+
assert_equal "*0.5555", f.write(Flt::DecNum('0.5555'))
|
841
|
+
f.set! padding: 6
|
842
|
+
assert_equal "0.5555", f.write(Flt::DecNum('0.5555'))
|
843
|
+
f.set! padding: 5
|
844
|
+
assert_equal "0.5555", f.write(Flt::DecNum('0.5555'))
|
845
|
+
f.set! padding: 4
|
846
|
+
assert_equal "0.5555", f.write(Flt::DecNum('0.5555'))
|
847
|
+
end
|
789
848
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: numerals
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Javier Goizueta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: flt
|
@@ -99,8 +99,9 @@ files:
|
|
99
99
|
- lib/numerals/format/notations/text.rb
|
100
100
|
- lib/numerals/format/output.rb
|
101
101
|
- lib/numerals/format/symbols.rb
|
102
|
+
- lib/numerals/format/symbols/digits.rb
|
103
|
+
- lib/numerals/format/symbols/padding.rb
|
102
104
|
- lib/numerals/format/text_parts.rb
|
103
|
-
- lib/numerals/formatting/options.rb
|
104
105
|
- lib/numerals/formatting_aspect.rb
|
105
106
|
- lib/numerals/numeral.rb
|
106
107
|
- lib/numerals/repeat_detector.rb
|
@@ -1,84 +0,0 @@
|
|
1
|
-
|
2
|
-
module Numerals
|
3
|
-
|
4
|
-
# Repeating decimal configuration options
|
5
|
-
class Options
|
6
|
-
include ModalSupport::StateEquivalent
|
7
|
-
include ModalSupport::BracketConstructor
|
8
|
-
|
9
|
-
def initialize(options={})
|
10
|
-
options = {
|
11
|
-
# default options
|
12
|
-
delim: ['<', '>'],
|
13
|
-
suffix: '...',
|
14
|
-
sep: '.',
|
15
|
-
grouping: [',', []], # [...,[3]] for thousands separators
|
16
|
-
special: ['NaN', 'Infinity'],
|
17
|
-
digits: nil,
|
18
|
-
signs: ['+', '-'],
|
19
|
-
maximum_number_of_digits: Numeral.maximum_number_of_digits
|
20
|
-
}.merge(options)
|
21
|
-
|
22
|
-
set_delim *Array(options[:delim])
|
23
|
-
set_suffix options[:suffix]
|
24
|
-
set_sep options[:sep]
|
25
|
-
set_grouping *Array(options[:grouping])
|
26
|
-
set_special *Array(options[:special])
|
27
|
-
set_digits *Array(options[:digits])
|
28
|
-
set_signs *Array(options[:signs])
|
29
|
-
@maximum_number_of_digits = options[:maximum_number_of_digits]
|
30
|
-
end
|
31
|
-
|
32
|
-
attr_accessor :begin_rep, :end_rep, :auto_rep, :dec_sep, :grp_sep, :grp, :maximum_number_of_digits
|
33
|
-
attr_accessor :nan_txt, :inf_txt, :plus_sign, :minus_sign
|
34
|
-
|
35
|
-
def set_delim(begin_d, end_d='')
|
36
|
-
@begin_rep = begin_d
|
37
|
-
@end_rep = end_d
|
38
|
-
return self
|
39
|
-
end
|
40
|
-
|
41
|
-
def set_suffix(a)
|
42
|
-
@auto_rep = a
|
43
|
-
return self
|
44
|
-
end
|
45
|
-
|
46
|
-
def set_sep(d)
|
47
|
-
@dec_sep = d
|
48
|
-
return self
|
49
|
-
end
|
50
|
-
|
51
|
-
def set_grouping(sep, g=[])
|
52
|
-
@grp_sep = sep
|
53
|
-
@grp = g
|
54
|
-
return self
|
55
|
-
end
|
56
|
-
|
57
|
-
def set_special(nan_txt, inf_txt)
|
58
|
-
@nan_txt = nan_txt
|
59
|
-
@inf_txt = inf_txt
|
60
|
-
return self
|
61
|
-
end
|
62
|
-
|
63
|
-
def set_digits(*args)
|
64
|
-
@digits_defined = !args.empty?
|
65
|
-
@digits = DigitsDefinition[*args]
|
66
|
-
self
|
67
|
-
end
|
68
|
-
|
69
|
-
def set_signs(plus, minus)
|
70
|
-
@plus_sign = plus
|
71
|
-
@minus_sign = minus
|
72
|
-
end
|
73
|
-
|
74
|
-
attr_accessor :digits
|
75
|
-
|
76
|
-
def digits_defined?
|
77
|
-
@digits_defined
|
78
|
-
end
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
DEFAULT_OPTIONS = Options[]
|
83
|
-
|
84
|
-
end
|