numbers_in_words 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +12 -0
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +1171 -0
- data/.travis.yml +7 -0
- data/Gemfile +1 -0
- data/README.md +34 -47
- data/bin/spec +2 -0
- data/lib/numbers_in_words.rb +9 -0
- data/lib/numbers_in_words/duck_punch.rb +2 -2
- data/lib/numbers_in_words/english/language_writer_english.rb +10 -8
- data/lib/numbers_in_words/number_parser.rb +1 -1
- data/lib/numbers_in_words/version.rb +1 -1
- data/numbers_in_words.gemspec +1 -2
- data/spec/language_writer_spec.rb +6 -6
- data/spec/non_monkey_patch_spec.rb +26 -0
- data/spec/number_group_spec.rb +7 -7
- data/spec/numbers_in_words_spec.rb +65 -38
- data/spec/spec_helper.rb +4 -0
- data/spec/words_in_numbers_spec.rb +72 -71
- metadata +29 -50
- data/Gemfile.lock +0 -39
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,14 @@
|
|
1
|
+
[![Build Status](http://img.shields.io/travis/markburns/numbers_in_words.svg)](https://travis-ci.org/markburns/numbers_in_words)
|
2
|
+
[![Dependency Status](http://img.shields.io/gemnasium/markburns/numbers_in_words.svg)](https://gemnasium.com/markburns/numbers_in_words)
|
3
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/markburns/numbers_in_words.svg)](https://codeclimate.com/github/markburns/numbers_in_words)
|
4
|
+
[![Gem Version](http://img.shields.io/gem/v/numbers_in_words.svg)](https://rubygems.org/gems/numbers_in_words)
|
5
|
+
[![License](http://img.shields.io/:license-mit-blue.svg)](http://markburns.mit-license.org)
|
6
|
+
[![Badges](http://img.shields.io/:badges-6/6-ff6799.svg)](https://github.com/badges/badgerbadgerbadger)
|
7
|
+
|
1
8
|
Installation
|
2
9
|
============
|
3
10
|
|
4
|
-
```
|
11
|
+
```ruby
|
5
12
|
gem 'numbers_in_words'
|
6
13
|
|
7
14
|
require 'numbers_in_words'
|
@@ -11,32 +18,24 @@ require 'numbers_in_words/duck_punch' #see why later
|
|
11
18
|
This project was created for a test for a job interview. I haven't really used
|
12
19
|
it myself, but I saw it mentioned somewhere so I thought I'd tidy it up a bit.
|
13
20
|
|
14
|
-
|
15
|
-
|
16
|
-
You've got one of:
|
21
|
+
Usage
|
22
|
+
=========
|
17
23
|
|
18
|
-
```
|
19
|
-
|
20
|
-
NoMethodError: undefined method `in_numbers' for "123":String
|
21
|
-
```
|
24
|
+
```ruby
|
25
|
+
require 'numbers_in_words'
|
22
26
|
|
23
|
-
|
24
|
-
|
27
|
+
NumbersInWords.in_words(112)
|
28
|
+
#=> one hundred and twelve
|
25
29
|
|
26
|
-
|
27
|
-
|
28
|
-
with:
|
30
|
+
NumbersInWords.in_numbers(112)
|
31
|
+
#=>10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
require 'numbers_in_words/duck_punch'
|
33
|
+
NumbersInWords.in_numbers("Seventy million, five-hundred and fifty six thousand point eight nine three")
|
34
|
+
#=> 70556000.893
|
33
35
|
```
|
34
36
|
|
35
|
-
Plus it now only adds a single `#in_words` method to `Numeric` and an `#in_numbers`
|
36
|
-
method to `String` instead of a whole bunch of them.
|
37
37
|
|
38
|
-
|
39
|
-
=========
|
38
|
+
Monkey patch version
|
40
39
|
|
41
40
|
```ruby
|
42
41
|
require 'numbers_in_words'
|
@@ -51,42 +50,30 @@ require 'numbers_in_words/duck_punch'
|
|
51
50
|
#=> 70556000.893
|
52
51
|
```
|
53
52
|
|
54
|
-
---------------
|
55
|
-
|
56
|
-
Whilst creating this project I realized that in English:
|
57
|
-
|
58
|
-
* Numbers are grouped in groups of threes
|
59
|
-
* Numbers less than 1,000 are grouped by hundreds and then by tens
|
60
|
-
* There are specific rules for when we put an "and" in between numbers
|
61
53
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
as the key and the multiplier as the value. E.g:
|
54
|
+
NoMethodError `in_words` or `in_numbers`
|
55
|
+
----------
|
56
|
+
I'm going to hopefully preempt some support queries by predicting this will happen:
|
66
57
|
|
67
|
-
|
68
|
-
31245.groups_of(3)
|
69
|
-
#=> {0=>245,3=>31} #i.e. 31 thousands, and 245 ones
|
58
|
+
You've got one of:
|
70
59
|
|
71
|
-
|
72
|
-
|
60
|
+
```
|
61
|
+
NoMethodError: undefined method `in_words' for 123:Fixnum
|
62
|
+
NoMethodError: undefined method `in_numbers' for "123":String
|
73
63
|
```
|
74
64
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
Example of usage:
|
65
|
+
Previous versions of this gem duckpunched Fixnum and String with a whole bunch
|
66
|
+
of methods. This gem will now only add methods if you specifically tell it to
|
67
|
+
with:
|
79
68
|
|
80
69
|
```ruby
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
70
|
+
require 'numbers_in_words'
|
71
|
+
require 'numbers_in_words/duck_punch'
|
72
|
+
```
|
85
73
|
|
86
|
-
|
87
|
-
|
74
|
+
Plus it now only adds a single `#in_words` method to `Numeric` and an `#in_numbers`
|
75
|
+
method to `String` instead of a whole bunch of them.
|
88
76
|
|
89
|
-
```
|
90
77
|
|
91
78
|
Future plans
|
92
79
|
============
|
data/bin/spec
ADDED
data/lib/numbers_in_words.rb
CHANGED
@@ -16,9 +16,18 @@ module NumbersInWords
|
|
16
16
|
|
17
17
|
class << self
|
18
18
|
attr_writer :language
|
19
|
+
|
19
20
|
def language
|
20
21
|
@language ||= "English"
|
21
22
|
end
|
23
|
+
|
24
|
+
def in_words(i, language=NumbersInWords.language)
|
25
|
+
NumbersInWords::ToWord.new(i, language).in_words
|
26
|
+
end
|
27
|
+
|
28
|
+
def in_numbers(s, language=NumbersInWords.language)
|
29
|
+
NumbersInWords::ToNumber.new(s, language).in_numbers
|
30
|
+
end
|
22
31
|
end
|
23
32
|
end
|
24
33
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module NumbersInWords
|
2
2
|
def in_words language=NumbersInWords.language
|
3
|
-
NumbersInWords
|
3
|
+
NumbersInWords.in_words(self, language)
|
4
4
|
end
|
5
5
|
end
|
6
6
|
|
7
7
|
module WordsInNumbers
|
8
8
|
def in_numbers language=NumbersInWords.language
|
9
|
-
NumbersInWords
|
9
|
+
NumbersInWords.in_numbers(self, language)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
@@ -9,7 +9,7 @@ module NumbersInWords
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def negative
|
12
|
-
"minus " + (-@that)
|
12
|
+
"minus " + NumbersInWords.in_words(-@that)
|
13
13
|
end
|
14
14
|
|
15
15
|
def in_words
|
@@ -33,13 +33,12 @@ module NumbersInWords
|
|
33
33
|
|
34
34
|
digit = number - tens #write the digits
|
35
35
|
|
36
|
-
output << " " +
|
36
|
+
output << " " + NumbersInWords.in_words(digit) unless digit==0
|
37
37
|
else
|
38
38
|
output << write() #longer numbers
|
39
39
|
end
|
40
40
|
|
41
41
|
output.strip
|
42
|
-
|
43
42
|
end
|
44
43
|
|
45
44
|
def handle_exception
|
@@ -67,24 +66,27 @@ module NumbersInWords
|
|
67
66
|
def decimals
|
68
67
|
int, decimals = NumberGroup.new(@that).split_decimals
|
69
68
|
if int
|
70
|
-
out =
|
69
|
+
out = NumbersInWords.in_words(int) + " point "
|
71
70
|
decimals.each do |decimal|
|
72
|
-
out << decimal.to_i
|
71
|
+
out << NumbersInWords.in_words(decimal.to_i) + " "
|
73
72
|
end
|
74
73
|
out.strip
|
75
74
|
end
|
76
75
|
end
|
77
76
|
|
78
77
|
private
|
78
|
+
|
79
79
|
def write_googols
|
80
80
|
googols, remainder = NumberGroup.new(@that).split_googols
|
81
81
|
output = ""
|
82
|
-
|
82
|
+
|
83
|
+
output << " " + NumbersInWords.in_words(googols) + " googol"
|
83
84
|
if remainder > 0
|
84
85
|
prefix = " "
|
85
86
|
prefix << "and " if remainder < 100
|
86
|
-
output << prefix +
|
87
|
+
output << prefix + NumbersInWords.in_words(remainder)
|
87
88
|
end
|
89
|
+
|
88
90
|
output
|
89
91
|
end
|
90
92
|
|
@@ -96,7 +98,7 @@ module NumbersInWords
|
|
96
98
|
prefix = " "
|
97
99
|
#no and between thousands and hundreds
|
98
100
|
prefix << "and " if power == 0 and digits < 100
|
99
|
-
output << prefix +
|
101
|
+
output << prefix + NumbersInWords.in_words(digits)
|
100
102
|
output << prefix + name unless power == 0
|
101
103
|
end
|
102
104
|
end
|
data/numbers_in_words.gemspec
CHANGED
@@ -14,8 +14,7 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.homepage = "http://github.com/markburns/numbers_in_words"
|
15
15
|
|
16
16
|
gem.add_dependency "activesupport"
|
17
|
-
gem.add_development_dependency "rspec"
|
18
|
-
gem.add_development_dependency "debugger"
|
17
|
+
gem.add_development_dependency "rspec", "~> 3.4.0"
|
19
18
|
|
20
19
|
gem.files = `git ls-files`.split($/)
|
21
20
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
@@ -7,13 +7,13 @@ describe NumbersInWords::English::LanguageWriterEnglish do
|
|
7
7
|
@writer.group_words(3) do |power, name, digits|
|
8
8
|
case count
|
9
9
|
when 0
|
10
|
-
power.
|
11
|
-
name.
|
12
|
-
digits.
|
10
|
+
expect(power).to eq(3)
|
11
|
+
expect(name).to eq("thousand")
|
12
|
+
expect(digits).to eq(2)
|
13
13
|
when 1
|
14
|
-
power.
|
15
|
-
name.
|
16
|
-
digits.
|
14
|
+
expect(power).to eq(0)
|
15
|
+
expect(name).to eq("one")
|
16
|
+
expect(digits).to eq(111)
|
17
17
|
end
|
18
18
|
count += 1
|
19
19
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'numbers_in_words'
|
2
|
+
|
3
|
+
describe "NumbersInWords" do
|
4
|
+
describe ".in_words" do
|
5
|
+
it do
|
6
|
+
expect(NumbersInWords.in_words(100)).to eq "one hundred"
|
7
|
+
expect(NumbersInWords.in_words(-100)).to eq "minus one hundred"
|
8
|
+
expect(NumbersInWords.in_words(24)).to eq "twenty four"
|
9
|
+
expect(NumbersInWords.in_words(1.2)).to eq "one point two"
|
10
|
+
expect(NumbersInWords.in_words(100*10**100)).to eq "one hundred googol"
|
11
|
+
expect(NumbersInWords.in_words(30 + 100*10**100)).to eq "one hundred googol and thirty"
|
12
|
+
end
|
13
|
+
|
14
|
+
pending "This should be implemented" do
|
15
|
+
expect(NumbersInWords.in_words(100*10**100)).to eq "one hundred googols"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe ".in_numbers" do
|
20
|
+
it do
|
21
|
+
expect(NumbersInWords.in_numbers("one hundred")).to eq 100
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
end
|
data/spec/number_group_spec.rb
CHANGED
@@ -4,13 +4,13 @@ describe NumbersInWords::NumberGroup do
|
|
4
4
|
it "should split into group of three digit numbers" do
|
5
5
|
g = Numeric::NumberGroup
|
6
6
|
|
7
|
-
g.groups_of(1 ,3 ).
|
8
|
-
g.groups_of(12 ,3 ).
|
9
|
-
g.groups_of(123 ,3 ).
|
10
|
-
g.groups_of(1111 ,3 ).
|
11
|
-
g.groups_of(87654 ,3 ).
|
12
|
-
g.groups_of(1_234_567 ,3 ).
|
13
|
-
g.groups_of(123_456_789_101_112 ,3 ).
|
7
|
+
expect(g.groups_of(1 ,3 )).to eq({0=>1 })
|
8
|
+
expect(g.groups_of(12 ,3 )).to eq({0=>12 })
|
9
|
+
expect(g.groups_of(123 ,3 )).to eq({0=>123 })
|
10
|
+
expect(g.groups_of(1111 ,3 )).to eq({3=>1 , 0=>111 })
|
11
|
+
expect(g.groups_of(87654 ,3 )).to eq({3=>87 , 0=>654 })
|
12
|
+
expect(g.groups_of(1_234_567 ,3 )).to eq({6=>1 , 3=>234 , 0=>567 })
|
13
|
+
expect(g.groups_of(123_456_789_101_112 ,3 )).to eq({12=>123 , 9=>456 , 6=>789 , 3=>101 , 0=>112 })
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -1,79 +1,106 @@
|
|
1
1
|
require './spec/spec_helper'
|
2
2
|
|
3
3
|
describe NumbersInWords do
|
4
|
+
describe ".language" do
|
5
|
+
after do
|
6
|
+
NumbersInWords.instance_variable_set(:"@language", nil)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "has a default value" do
|
10
|
+
expect(NumbersInWords.language).to eq "English"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can be set" do
|
14
|
+
NumbersInWords.language = "some language"
|
15
|
+
expect(NumbersInWords.language).to eq "some language"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe ".in_words" do
|
20
|
+
it do
|
21
|
+
expect(NumbersInWords.in_words(100)).to eq "one hundred"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe ".in_numbers" do
|
26
|
+
it do
|
27
|
+
expect(NumbersInWords.in_numbers("one hundred")).to eq 100
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
4
31
|
it "should print the digits 0-9 correctly" do
|
5
32
|
numbers = %w[zero one two three four five six seven eight nine]
|
6
33
|
|
7
|
-
10.times { |i| i.in_words.
|
34
|
+
10.times { |i| expect(i.in_words).to eq(numbers[i]) }
|
8
35
|
end
|
9
36
|
|
10
37
|
it "should print the digits 11-19 correctly" do
|
11
38
|
words = %w[eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen]
|
12
39
|
numbers = [11,12,13,14,15,16,17,18,19]
|
13
40
|
numbers.each_with_index do |number, index|
|
14
|
-
number.in_words.
|
41
|
+
expect(number.in_words).to eq(words[index])
|
15
42
|
|
16
43
|
end
|
17
44
|
end
|
18
45
|
|
19
46
|
it "should handle two digit numbers" do
|
20
|
-
21.in_words.
|
47
|
+
expect(21.in_words). to eq("twenty one")
|
21
48
|
end
|
22
49
|
|
23
50
|
it "should handle three digit numbers" do
|
24
|
-
113.in_words.
|
25
|
-
299.in_words.
|
26
|
-
300.in_words.
|
27
|
-
101.in_words.
|
51
|
+
expect(113.in_words). to eq("one hundred and thirteen")
|
52
|
+
expect(299.in_words). to eq("two hundred and ninety nine")
|
53
|
+
expect(300.in_words). to eq("three hundred")
|
54
|
+
expect(101.in_words). to eq("one hundred and one")
|
28
55
|
end
|
29
56
|
|
30
57
|
it "should print out some random examples correctly" do
|
31
|
-
2999 .in_words.
|
32
|
-
99999 .in_words.
|
33
|
-
999999 .in_words.
|
34
|
-
123456 .in_words.
|
35
|
-
17054 .in_words.
|
36
|
-
11004 .in_words.
|
37
|
-
470154 .in_words.
|
38
|
-
417155 .in_words.
|
39
|
-
999999 .in_words.
|
40
|
-
1000000 .in_words.
|
41
|
-
1000001 .in_words.
|
42
|
-
112 .in_words.
|
58
|
+
expect(2999 .in_words). to eq("two thousand nine hundred and ninety nine")
|
59
|
+
expect(99999 .in_words). to eq("ninety nine thousand nine hundred and ninety nine")
|
60
|
+
expect(999999 .in_words). to eq("nine hundred and ninety nine thousand nine hundred and ninety nine")
|
61
|
+
expect(123456 .in_words). to eq("one hundred and twenty three thousand four hundred and fifty six")
|
62
|
+
expect(17054 .in_words). to eq("seventeen thousand and fifty four")
|
63
|
+
expect(11004 .in_words). to eq("eleven thousand and four")
|
64
|
+
expect(470154 .in_words). to eq("four hundred and seventy thousand one hundred and fifty four")
|
65
|
+
expect(417155 .in_words). to eq("four hundred and seventeen thousand one hundred and fifty five")
|
66
|
+
expect(999999 .in_words). to eq("nine hundred and ninety nine thousand nine hundred and ninety nine")
|
67
|
+
expect(1000000 .in_words). to eq("one million")
|
68
|
+
expect(1000001 .in_words). to eq("one million and one")
|
69
|
+
expect(112 .in_words). to eq("one hundred and twelve")
|
43
70
|
end
|
44
71
|
|
45
72
|
it "should handle edge cases" do
|
46
|
-
1000001 .in_words.
|
47
|
-
(10*10**12 + 10**6 +1) .in_words.
|
48
|
-
(10**75) .in_words.
|
49
|
-
10001001 .in_words.
|
73
|
+
expect(1000001 .in_words). to eq("one million and one")
|
74
|
+
expect((10*10**12 + 10**6 +1) .in_words). to eq("ten trillion one million and one")
|
75
|
+
expect((10**75) .in_words). to eq("one quattuorvigintillion")
|
76
|
+
expect(10001001 .in_words). to eq("ten million one thousand and one")
|
50
77
|
end
|
51
78
|
|
52
79
|
it "should handle a googol and larger" do
|
53
80
|
n=10**100
|
54
|
-
(10**100 + 1) .in_words.
|
55
|
-
(42*10**100 + 16777216).in_words.
|
56
|
-
(42* 10**100 * 10**100).in_words.
|
81
|
+
expect((10**100 + 1) .in_words). to eq("one googol and one")
|
82
|
+
expect((42*10**100 + 16777216).in_words). to eq("forty two googol sixteen million seven hundred and seventy seven thousand two hundred and sixteen")
|
83
|
+
expect((42* 10**100 * 10**100).in_words). to eq("forty two googol googol")
|
57
84
|
end
|
58
85
|
|
59
86
|
it "should handle negative numbers" do
|
60
|
-
-1 .in_words.
|
61
|
-
-9 .in_words.
|
62
|
-
-10 .in_words.
|
63
|
-
-15 .in_words.
|
64
|
-
-100 .in_words.
|
65
|
-
(-1*(10**100)).in_words.
|
66
|
-
-123456789 .in_words.
|
87
|
+
expect(-1 .in_words).to eq("minus one")
|
88
|
+
expect(-9 .in_words).to eq("minus nine")
|
89
|
+
expect(-10 .in_words).to eq("minus ten")
|
90
|
+
expect(-15 .in_words).to eq("minus fifteen")
|
91
|
+
expect(-100 .in_words).to eq("minus one hundred")
|
92
|
+
expect((-1*(10**100)).in_words).to eq("minus one googol")
|
93
|
+
expect(-123456789 .in_words).to eq("minus one hundred and twenty three million four hundred and fifty six thousand seven hundred and eighty nine")
|
67
94
|
end
|
68
95
|
|
69
96
|
it "should handle decimals" do
|
70
97
|
#because of lack of absolute accuracy with floats
|
71
98
|
#the output won't be exactly as you might expect
|
72
99
|
#so we will match rather than find equivalents
|
73
|
-
1.1 .in_words.
|
74
|
-
1.2345678 .in_words.
|
75
|
-
1000.2345678 .in_words.
|
76
|
-
12345.2345678.in_words.
|
77
|
-
(10**9 + 0.1).in_words.
|
100
|
+
expect(1.1 .in_words).to match(/one point one/)
|
101
|
+
expect(1.2345678 .in_words).to match(/one point two three four five six seven eight/)
|
102
|
+
expect(1000.2345678 .in_words).to match(/one thousand point two three four five six seven eight/)
|
103
|
+
expect(12345.2345678.in_words).to match(/twelve thousand three hundred and forty five point two three four five six seven eight/)
|
104
|
+
expect((10**9 + 0.1).in_words).to match(/one billion point one/)
|
78
105
|
end
|
79
106
|
end
|