numbers_in_words 0.1.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +14 -0
  3. data/.gitignore +7 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +58 -0
  6. data/.travis.yml +15 -0
  7. data/Gemfile +8 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +47 -0
  10. data/Rakefile +2 -43
  11. data/bin/spec +2 -0
  12. data/lib/numbers_in_words.rb +55 -4
  13. data/lib/numbers_in_words/duck_punch.rb +23 -0
  14. data/lib/numbers_in_words/exceptional_numbers.rb +115 -0
  15. data/lib/numbers_in_words/fraction.rb +136 -0
  16. data/lib/numbers_in_words/number_group.rb +64 -0
  17. data/lib/numbers_in_words/parsing/fraction_parsing.rb +34 -0
  18. data/lib/numbers_in_words/parsing/number_parser.rb +98 -0
  19. data/lib/numbers_in_words/parsing/pair_parsing.rb +64 -0
  20. data/lib/numbers_in_words/parsing/parse_fractions.rb +45 -0
  21. data/lib/numbers_in_words/parsing/parse_individual_number.rb +68 -0
  22. data/lib/numbers_in_words/parsing/parse_status.rb +17 -0
  23. data/lib/numbers_in_words/parsing/special.rb +67 -0
  24. data/lib/numbers_in_words/parsing/to_number.rb +77 -0
  25. data/lib/numbers_in_words/powers_of_ten.rb +49 -0
  26. data/lib/numbers_in_words/to_word.rb +84 -0
  27. data/lib/numbers_in_words/version.rb +5 -0
  28. data/lib/numbers_in_words/writer.rb +69 -0
  29. data/numbers_in_words.gemspec +20 -27
  30. data/spec/exceptional_numbers_spec.rb +26 -0
  31. data/spec/fraction_spec.rb +152 -0
  32. data/spec/fractions_spec.rb +31 -0
  33. data/spec/non_monkey_patch_spec.rb +51 -0
  34. data/spec/number_group_spec.rb +17 -0
  35. data/spec/number_parser_spec.rb +31 -0
  36. data/spec/numbers_in_words_spec.rb +69 -83
  37. data/spec/numerical_strings_spec.rb +35 -0
  38. data/spec/spec_helper.rb +26 -0
  39. data/spec/to_word_spec.rb +18 -0
  40. data/spec/words_in_numbers_spec.rb +137 -119
  41. data/spec/writer_spec.rb +26 -0
  42. data/spec/years_spec.rb +27 -0
  43. metadata +95 -45
  44. data/CHANGELOG +0 -1
  45. data/Manifest +0 -11
  46. data/README +0 -84
  47. data/examples/display_numbers_in_words.rb +0 -22
  48. data/init.rb +0 -8
  49. data/lib/numbers.rb +0 -260
  50. data/lib/words.rb +0 -221
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NumbersInWords::Fraction do # rubocop: disable Metrics/BlockLength
4
+ subject do
5
+ described_class.new(numerator: numerator, denominator: denominator, attributes: attributes)
6
+ end
7
+
8
+ let(:numerator) { 1 }
9
+
10
+ context 'halves' do
11
+ let(:denominator) { 2 }
12
+ let(:attributes) do
13
+ { number: 'two',
14
+ ordinal: 'second',
15
+ fraction: { singular: 'half', plural: 'halves' } }
16
+ end
17
+
18
+ it do
19
+ expect(subject.in_words).to eq 'one half'
20
+ end
21
+
22
+ context 'with plural' do
23
+ let(:numerator) { 2 }
24
+
25
+ it do
26
+ expect(subject.in_words).to eq 'two halves'
27
+ end
28
+ end
29
+ end
30
+
31
+ context 'quarters' do
32
+ let(:denominator) { 4 }
33
+ let(:attributes) do
34
+ {
35
+ number: 'four',
36
+ ordinal: 'fourth',
37
+ fraction: { singular: 'quarter' }
38
+ }
39
+ end
40
+
41
+ it do
42
+ expect(subject.in_words).to eq 'one quarter'
43
+ end
44
+ end
45
+
46
+ context 'fifths' do
47
+ let(:denominator) { 5 }
48
+ let(:attributes) do
49
+ { number: 'five', ordinal: 'fifth' }
50
+ end
51
+
52
+ it do
53
+ expect(subject.in_words).to eq 'one fifth'
54
+ end
55
+ end
56
+
57
+ context 'sixths' do
58
+ let(:denominator) { 6 }
59
+ let(:attributes) { {} }
60
+
61
+ it do
62
+ expect(subject.in_words).to eq 'one sixth'
63
+ end
64
+ end
65
+
66
+ context 'nineteenths' do
67
+ let(:denominator) { 19 }
68
+ let(:attributes) { {} }
69
+
70
+ it do
71
+ expect(subject.in_words).to eq 'one nineteenth'
72
+ expect(subject.fraction).to eq 'nineteenth'
73
+ end
74
+
75
+ context 'plural' do
76
+ let(:numerator) { 763 }
77
+
78
+ it do
79
+ expect(subject.in_words).to eq 'seven hundred and sixty-three nineteenths'
80
+ end
81
+ end
82
+ end
83
+
84
+ context 'one hundred and seconds' do
85
+ let(:denominator) { 102 }
86
+ let(:attributes) { {} }
87
+
88
+ it do
89
+ expect(subject.in_words).to eq 'one one hundred and second'
90
+ end
91
+ end
92
+
93
+ context 'one hundred and sixth' do
94
+ let(:denominator) { 106 }
95
+ let(:attributes) { {} }
96
+
97
+ it do
98
+ expect(subject.in_words).to eq 'one one hundred and sixth'
99
+ end
100
+ end
101
+
102
+ context 'one hundred and nineteenth' do
103
+ let(:denominator) { 119 }
104
+ let(:attributes) { {} }
105
+
106
+ it do
107
+ expect(subject.ordinal).to eq 'one hundred and nineteenth'
108
+ expect(subject.in_words).to eq 'one one hundred and nineteenth'
109
+ end
110
+ end
111
+
112
+ context 'one thousandth' do
113
+ let(:denominator) { 1000 }
114
+ let(:attributes) { {} }
115
+
116
+ it do
117
+ expect(subject.ordinal).to eq 'one thousandth'
118
+ expect(subject.in_words).to eq 'one one thousandth'
119
+ end
120
+
121
+ context 'plural' do
122
+ let(:numerator) { 2 }
123
+ let(:denominator) { 1000 }
124
+
125
+ it do
126
+ expect(subject.in_words).to eq 'two thousandths'
127
+ end
128
+ end
129
+ end
130
+
131
+ context 'googolplexths' do
132
+ let(:denominator) { Kernel.silence_warnings { 10**(10**100) } }
133
+
134
+ let(:attributes) do
135
+ { number: 'googolplex',
136
+ ordinal: 'googolplexth',
137
+ fraction: { singular: 'googolplexth', plural: 'googolplexths' } }
138
+ end
139
+
140
+ it do
141
+ expect(subject.in_words).to eq 'one infinitieth'
142
+ end
143
+
144
+ context 'with plural' do
145
+ let(:numerator) { 2 }
146
+
147
+ it do
148
+ expect(subject.in_words).to eq 'two infinitieths'
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe NumbersInWords do
4
+ FRACTION_EXAMPLES = {
5
+ 'half' => 0.5,
6
+ 'a half' => 0.5,
7
+ 'one half' => 0.5,
8
+ 'two halves' => 1.0,
9
+ 'three halves' => 1.5,
10
+ 'three quarters' => 0.75,
11
+ 'three fifths' => 3 / 5.0,
12
+ 'seven fifteenths' => 7 / 15.0
13
+ }.freeze
14
+
15
+ PENDING = {
16
+ 'twenty and three fifteenths' => 20 + 3 / 15.0,
17
+ 'three ninety-sevenths' => 3 / 97.0
18
+ }.freeze
19
+
20
+ FRACTION_EXAMPLES.each do |string, float|
21
+ it "#{string} == #{float}" do
22
+ expect(string.in_numbers).to eql(float)
23
+ end
24
+ end
25
+
26
+ PENDING.each do |string, float|
27
+ pending "#{string} == #{float}" do
28
+ expect(string.in_numbers).to eql(float)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'NumbersInWords' do # rubocop: disable Metrics/BlockLength
6
+ describe '.in_words' do
7
+ it do
8
+ expect(NumbersInWords.in_words(100)).to eq 'one hundred'
9
+ expect(NumbersInWords.in_words(-100)).to eq 'minus one hundred'
10
+ expect(NumbersInWords.in_words(24)).to eq 'twenty-four'
11
+ expect(NumbersInWords.in_words(1.2)).to eq 'one point two'
12
+ expect(NumbersInWords.in_words(100 * 10**100)).to eq 'one hundred googol'
13
+ expect(NumbersInWords.in_words(30 + 100 * 10**100)).to eq 'one hundred googol and thirty'
14
+ end
15
+ end
16
+
17
+ FRACTION_TO_WORDS = {
18
+ [1, 2] => 'one half',
19
+ [3, 2] => 'three halves',
20
+ [1, 3] => 'one third',
21
+ [1, 4] => 'one quarter',
22
+ [1, 5] => 'one fifth',
23
+ [1, 19] => 'one nineteenth',
24
+ [2, 17] => 'two seventeenths',
25
+ [1, 21] => 'one twenty-first',
26
+ [1, 32] => 'one thirty-second',
27
+ [1, 101] => 'one one hundred and first',
28
+ [3, 101] => 'three hundred and firsts',
29
+ [73, 102] => 'seventy-three hundred and seconds',
30
+ [13, 97] => 'thirteen ninety-sevenths'
31
+ }.freeze
32
+
33
+ FRACTION_TO_WORDS.each do |(numerator, denominator), string|
34
+ it "#{numerator}/#{denominator} == #{string}" do
35
+ expect(NumbersInWords.in_words(numerator.to_f / denominator, fraction: true)).to eql(string)
36
+ end
37
+ end
38
+
39
+ describe '.in_numbers' do
40
+ it do
41
+ expect(NumbersInWords.in_numbers('hundred')).to eq 100
42
+ expect(NumbersInWords.in_numbers('one hundred')).to eq 100
43
+
44
+ expect(NumbersInWords.in_numbers('minus one hundred')).to eq(-100)
45
+ expect(NumbersInWords.in_numbers('twenty four')).to eq 24
46
+ expect(NumbersInWords.in_numbers('one point two')).to eq 1.2
47
+ expect(NumbersInWords.in_numbers('one hundred googol')).to eq 100 * 10**100
48
+ expect(NumbersInWords.in_numbers('one hundred googol and thirty')).to eq 30 + 100 * 10**100
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require './spec/spec_helper'
4
+ require 'numbers_in_words/number_group'
5
+
6
+ describe NumbersInWords::NumberGroup do
7
+ it 'should split into group of three digit numbers' do
8
+ g = described_class
9
+ expect(g.groups_of(1, 3)).to eq({ 0 => 1 })
10
+ expect(g.groups_of(12, 3)).to eq({ 0 => 12 })
11
+ expect(g.groups_of(123, 3)).to eq({ 0 => 123 })
12
+ expect(g.groups_of(1111, 3)).to eq({ 3 => 1, 0 => 111 })
13
+ expect(g.groups_of(87_654, 3)).to eq({ 3 => 87, 0 => 654 })
14
+ expect(g.groups_of(1_234_567, 3)).to eq({ 6 => 1, 3 => 234, 0 => 567 })
15
+ expect(g.groups_of(123_456_789_101_112, 3)).to eq({ 12 => 123, 9 => 456, 6 => 789, 3 => 101, 0 => 112 })
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require './spec/spec_helper'
4
+ require 'numbers_in_words/parsing/number_parser'
5
+
6
+ describe NumbersInWords::NumberParser do
7
+ subject do
8
+ super().parse(number)
9
+ end
10
+
11
+ context 'with single digit' do
12
+ let(:number) { [9] }
13
+ it { expect(subject).to eq 9 }
14
+ end
15
+
16
+ context 'with thousands' do
17
+ let(:number) { [20, 1000] }
18
+ it { expect(subject).to eq 20_000 }
19
+ end
20
+
21
+ context 'with thousands with a leading one' do
22
+ let(:number) { [1, 1000, 6, 100] }
23
+ it { expect(subject).to eq 1600 }
24
+ end
25
+
26
+ context 'with hundreds' do
27
+ let(:number) { [2, 100, 45] }
28
+
29
+ it { expect(subject).to eq 245 }
30
+ end
31
+ end
@@ -1,113 +1,99 @@
1
- require './lib/numbers_in_words'
1
+ # frozen_string_literal: true
2
2
 
3
- describe NumbersInWords::NumberGroup do
3
+ require './spec/spec_helper'
4
4
 
5
- it "should split into group of three digit numbers" do
6
- Numeric::NumberGroup.groups_of(1,3).should == {0=>1}
7
- Numeric::NumberGroup.groups_of(12,3).should == {0=>12}
8
- Numeric::NumberGroup.groups_of(123,3).should == {0=>123}
9
- Numeric::NumberGroup.groups_of(1111,3).should == {3=>1,0=>111}
10
- Numeric::NumberGroup.groups_of(87654,3).should == {3=>87,0=>654}
11
- Numeric::NumberGroup.groups_of(1234567,3).should == {6=>1,3=>234,0=>567}
12
- Numeric::NumberGroup.groups_of(123456789101112,3).should == {12=>123,9=>456,6=>789,3=>101,0=>112}
5
+ describe NumbersInWords do # rubocop: disable Metrics/BlockLength
6
+ describe '.in_words' do
7
+ it do
8
+ expect(NumbersInWords.in_words(100)).to eq 'one hundred'
9
+ end
13
10
  end
14
- end
15
11
 
16
- describe NumbersInWords::LanguageWriterEnglish do
17
- it "should display numbers grouped" do
18
- count = 0
19
- @writer = NumbersInWords::LanguageWriterEnglish.new(2111)
20
- @writer.group_words(3) do |power, name, digits|
21
- case count
22
- when 0
23
- power.should == 3
24
- name.should == "thousand"
25
- digits.should == 2
26
- when 1
27
- power.should == 0
28
- name.should == "one"
29
- digits.should == 111
30
- end
31
- count += 1
12
+ describe '.in_numbers' do
13
+ it do
14
+ expect(NumbersInWords.in_numbers('one-hundred')).to eq 100
32
15
  end
33
16
  end
34
- end
35
-
36
17
 
37
- describe NumbersInWords do
38
- it "should print the digits 0-9 correctly" do
18
+ it 'should print the digits 0-9 correctly' do
39
19
  numbers = %w[zero one two three four five six seven eight nine]
40
20
 
41
- 10.times { |i| i.in_words.should==numbers[i] }
21
+ 10.times { |i| expect(i.in_words).to eq(numbers[i]) }
42
22
  end
43
23
 
44
- it "should print the digits 11-19 correctly" do
24
+ it 'should print the digits 11-19 correctly' do
45
25
  words = %w[eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen]
46
- numbers = [11,12,13,14,15,16,17,18,19]
26
+ numbers = [11, 12, 13, 14, 15, 16, 17, 18, 19]
47
27
  numbers.each_with_index do |number, index|
48
- number.in_words.should==words[index]
49
-
28
+ expect(number.in_words).to eq(words[index])
50
29
  end
51
30
  end
52
31
 
53
- it "should handle two digit numbers" do
54
- 21.in_words. should == "twenty one"
32
+ it 'should handle two digit numbers' do
33
+ expect(21.in_words).to eq('twenty-one')
55
34
  end
56
35
 
57
- it "should handle three digit numbers" do
58
- 113.in_words. should == "one hundred and thirteen"
59
- 299.in_words. should == "two hundred and ninety nine"
60
- 300.in_words. should == "three hundred"
61
- 101.in_words. should == "one hundred and one"
36
+ it 'should handle three digit numbers' do
37
+ expect(113.in_words).to eq('one hundred and thirteen')
38
+ expect(299.in_words).to eq('two hundred and ninety-nine')
39
+ expect(300.in_words).to eq('three hundred')
40
+ expect(101.in_words).to eq('one hundred and one')
62
41
  end
63
42
 
64
- it "should print out some random examples correctly" do
65
- 2999 .in_words. should == "two thousand nine hundred and ninety nine"
66
- 99999 .in_words. should == "ninety nine thousand nine hundred and ninety nine"
67
- 999999 .in_words. should == "nine hundred and ninety nine thousand nine hundred and ninety nine"
68
- 123456 .in_words. should == "one hundred and twenty three thousand four hundred and fifty six"
69
- 17054 .in_words. should == "seventeen thousand and fifty four"
70
- 11004 .in_words. should == "eleven thousand and four"
71
- 470154 .in_words. should == "four hundred and seventy thousand one hundred and fifty four"
72
- 417155 .in_words. should == "four hundred and seventeen thousand one hundred and fifty five"
73
- 999999 .in_words. should == "nine hundred and ninety nine thousand nine hundred and ninety nine"
74
- 1000000 .in_words. should == "one million"
75
- 1000001 .in_words. should == "one million and one"
76
- 112 .in_words. should == "one hundred and twelve"
43
+ it 'should print out some random examples correctly' do
44
+ expect(2999.in_words).to eq('two thousand nine hundred and ninety-nine')
45
+ expect(99_999.in_words).to eq('ninety-nine thousand nine hundred and ninety-nine')
46
+ expect(999_999.in_words).to eq('nine hundred and ninety-nine thousand nine hundred and ninety-nine')
47
+ expect(123_456.in_words).to eq('one hundred and twenty-three thousand four hundred and fifty-six')
48
+ expect(24_056.in_words).to eq('twenty-four thousand and fifty-six')
49
+ expect(17_054.in_words).to eq('seventeen thousand and fifty-four')
50
+ expect(11_004.in_words).to eq('eleven thousand and four')
51
+ expect(470_154.in_words).to eq('four hundred and seventy thousand one hundred and fifty-four')
52
+ expect(417_155.in_words).to eq('four hundred and seventeen thousand one hundred and fifty-five')
53
+ expect(999_999.in_words).to eq('nine hundred and ninety-nine thousand nine hundred and ninety-nine')
54
+ expect(1_000_000.in_words).to eq('one million')
55
+ expect(1_000_001.in_words).to eq('one million and one')
56
+ expect(112.in_words).to eq('one hundred and twelve')
77
57
  end
78
58
 
79
- it "should handle edge cases" do
80
- 1000001 .in_words. should == "one million and one"
81
- (10*10**12 + 10**6 +1) .in_words. should == "ten trillion one million and one"
82
- (10**75) .in_words. should == "one quattuorvigintillion"
83
- 10001001 .in_words. should == "ten million one thousand and one"
59
+ it 'should handle edge cases' do
60
+ expect(1_000_001.in_words).to eq('one million and one')
61
+ expect((10 * 10**12 + 10**6 + 1).in_words).to eq('ten trillion one million and one')
62
+ expect((10**75).in_words).to eq('one quattuorvigintillion')
63
+ expect(10_001_001.in_words).to eq('ten million one thousand and one')
84
64
  end
85
65
 
86
- it "should handle a googol and larger" do
87
- n=10**100
88
- (10**100 + 1) .in_words. should == "one googol and one"
89
- (42*10**100 + 16777216).in_words. should == "forty two googol sixteen million seven hundred and seventy seven thousand two hundred and sixteen"
90
- (42* 10**100 * 10**100).in_words. should == "forty two googol googol"
66
+ it 'should handle a googol and larger' do
67
+ googol = 10**100
68
+ expect((googol + 1).in_words).to eq('one googol and one')
69
+ expect((42 * googol + 16_777_216).in_words)
70
+ .to eq('forty-two googol sixteen million seven hundred and seventy-seven thousand two hundred and sixteen')
71
+ expect((42 * googol * googol).in_words).to eq('forty-two googol googol')
91
72
  end
92
73
 
93
- it "should handle negative numbers" do
94
- -1 .in_words.should == "minus one"
95
- -9 .in_words.should == "minus nine"
96
- -10 .in_words.should == "minus ten"
97
- -15 .in_words.should == "minus fifteen"
98
- -100 .in_words.should == "minus one hundred"
99
- (-1*(10**100)).in_words.should == "minus one googol"
100
- -123456789 .in_words.should == "minus one hundred and twenty three million four hundred and fifty six thousand seven hundred and eighty nine"
74
+ it 'should handle negative numbers' do
75
+ expect(-1.in_words).to eq('minus one')
76
+ expect(-9.in_words).to eq('minus nine')
77
+ expect(-10.in_words).to eq('minus ten')
78
+ expect(-15.in_words).to eq('minus fifteen')
79
+ expect(-100.in_words).to eq('minus one hundred')
80
+ expect((-1 * (10**100)).in_words).to eq('minus one googol')
81
+
82
+ expect(-123_456_789.in_words)
83
+ .to eq('minus one hundred and twenty-three million four hundred and ' \
84
+ 'fifty-six thousand seven hundred and eighty-nine')
101
85
  end
102
86
 
103
- it "should handle decimals" do
104
- #because of lack of absolute accuracy with floats
105
- #the output won't be exactly as you might expect
106
- #so we will match rather than find equivalents
107
- 1.1 .in_words.should =~ /one point one/
108
- 1.2345678 .in_words.should =~ /one point two three four five six seven eight/
109
- 1000.2345678 .in_words.should =~ /one thousand point two three four five six seven eight/
110
- 12345.2345678.in_words.should =~ /twelve thousand three hundred and forty five point two three four five six seven eight/
111
- (10**9 + 0.1).in_words.should =~ /one billion point one/
87
+ it 'should handle decimals' do
88
+ # because of lack of absolute accuracy with floats
89
+ # the output won't be exactly as you might expect
90
+ # so we will match rather than find equivalents
91
+ expect(1.1.in_words).to match(/one point one/)
92
+ expect(1.2345678.in_words).to match(/one point two three four five six seven eight/)
93
+ expect(1000.2345678.in_words).to match(/one thousand point two three four five six seven eight/)
94
+ expect(12_345.2345678.in_words)
95
+ .to match(/twelve thousand three hundred and forty-five point two three four five six seven eight/)
96
+
97
+ expect((10**9 + 0.1).in_words).to match(/one billion point one/)
112
98
  end
113
99
  end