numbers_in_words 0.1.1 → 0.5.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 +7 -0
- data/.codeclimate.yml +14 -0
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.rubocop.yml +58 -0
- data/.travis.yml +15 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +2 -43
- data/bin/spec +2 -0
- data/lib/numbers_in_words.rb +55 -4
- data/lib/numbers_in_words/duck_punch.rb +23 -0
- data/lib/numbers_in_words/exceptional_numbers.rb +115 -0
- data/lib/numbers_in_words/fraction.rb +136 -0
- data/lib/numbers_in_words/number_group.rb +64 -0
- data/lib/numbers_in_words/parsing/fraction_parsing.rb +34 -0
- data/lib/numbers_in_words/parsing/number_parser.rb +98 -0
- data/lib/numbers_in_words/parsing/pair_parsing.rb +64 -0
- data/lib/numbers_in_words/parsing/parse_fractions.rb +45 -0
- data/lib/numbers_in_words/parsing/parse_individual_number.rb +68 -0
- data/lib/numbers_in_words/parsing/parse_status.rb +17 -0
- data/lib/numbers_in_words/parsing/special.rb +67 -0
- data/lib/numbers_in_words/parsing/to_number.rb +77 -0
- data/lib/numbers_in_words/powers_of_ten.rb +49 -0
- data/lib/numbers_in_words/to_word.rb +84 -0
- data/lib/numbers_in_words/version.rb +5 -0
- data/lib/numbers_in_words/writer.rb +69 -0
- data/numbers_in_words.gemspec +20 -27
- data/spec/exceptional_numbers_spec.rb +26 -0
- data/spec/fraction_spec.rb +152 -0
- data/spec/fractions_spec.rb +31 -0
- data/spec/non_monkey_patch_spec.rb +51 -0
- data/spec/number_group_spec.rb +17 -0
- data/spec/number_parser_spec.rb +31 -0
- data/spec/numbers_in_words_spec.rb +69 -83
- data/spec/numerical_strings_spec.rb +35 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/to_word_spec.rb +18 -0
- data/spec/words_in_numbers_spec.rb +137 -119
- data/spec/writer_spec.rb +26 -0
- data/spec/years_spec.rb +27 -0
- metadata +95 -45
- data/CHANGELOG +0 -1
- data/Manifest +0 -11
- data/README +0 -84
- data/examples/display_numbers_in_words.rb +0 -22
- data/init.rb +0 -8
- data/lib/numbers.rb +0 -260
- data/lib/words.rb +0 -221
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5a69b94b2d308dbf461d439c03e57ab585f6bd14760fbe01fbfb72ab7c87bf49
|
4
|
+
data.tar.gz: e8eaf38c1c28fa6b6d6c055ad2e73a0a446f418f984314682c5cc22eda6a64a3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a62e3d4ee01401dc3fd9ed1a694722fc4033954ea4c3e4ffc0f321f2c11777b91b465407873a80477986ef4c91d6bbc1aaa64dd6b0d9ab35ff45435d687b09a4
|
7
|
+
data.tar.gz: f068adee24754761e40e8b969fd432f478ec6c0d921e686594a33c29d06c4384644b2cb9a18a82c385e225e090c0a3db2fb379154d5885ced72523efb9f1776c
|
data/.codeclimate.yml
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# The behavior of RuboCop can be controlled via the .rubocop.yml
|
2
|
+
# configuration file. It makes it possible to enable/disable
|
3
|
+
# certain cops (checks) and to alter their behavior if they accept
|
4
|
+
# any parameters. The file can be placed either in your home
|
5
|
+
# directory or in some project directory.
|
6
|
+
#
|
7
|
+
# RuboCop will start looking for the configuration file in the directory
|
8
|
+
# where the inspected file is and continue its way up to the root directory.
|
9
|
+
#
|
10
|
+
# See https://docs.rubocop.org/rubocop/configuration
|
11
|
+
Layout/EmptyLinesAroundAttributeAccessor:
|
12
|
+
Enabled: true
|
13
|
+
Layout/SpaceAroundMethodCallOperator:
|
14
|
+
Enabled: true
|
15
|
+
Lint/DeprecatedOpenSSLConstant:
|
16
|
+
Enabled: true
|
17
|
+
Lint/MixedRegexpCaptureTypes:
|
18
|
+
Enabled: true
|
19
|
+
Lint/RaiseException:
|
20
|
+
Enabled: true
|
21
|
+
Lint/StructNewOverride:
|
22
|
+
Enabled: true
|
23
|
+
Style/ExponentialNotation:
|
24
|
+
Enabled: true
|
25
|
+
Style/HashEachMethods:
|
26
|
+
Enabled: true
|
27
|
+
Style/HashTransformKeys:
|
28
|
+
Enabled: true
|
29
|
+
Style/HashTransformValues:
|
30
|
+
Enabled: true
|
31
|
+
Style/RedundantFetchBlock:
|
32
|
+
Enabled: true
|
33
|
+
Style/RedundantRegexpCharacterClass:
|
34
|
+
Enabled: true
|
35
|
+
Style/RedundantRegexpEscape:
|
36
|
+
Enabled: true
|
37
|
+
Style/SlicingWithRange:
|
38
|
+
Enabled: true
|
39
|
+
Lint/DuplicateElsifCondition:
|
40
|
+
Enabled: true
|
41
|
+
Style/AccessorGrouping:
|
42
|
+
Enabled: true
|
43
|
+
Style/ArrayCoercion:
|
44
|
+
Enabled: true
|
45
|
+
Style/BisectedAttrAccessor:
|
46
|
+
Enabled: true
|
47
|
+
Style/CaseLikeIf:
|
48
|
+
Enabled: true
|
49
|
+
Style/Documentation:
|
50
|
+
Enabled: false
|
51
|
+
Style/HashAsLastArrayItem:
|
52
|
+
Enabled: true
|
53
|
+
Style/HashLikeCase:
|
54
|
+
Enabled: true
|
55
|
+
Style/RedundantAssignment:
|
56
|
+
Enabled: true
|
57
|
+
Style/RedundantFileExtensionInRequire:
|
58
|
+
Enabled: true
|
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
env:
|
2
|
+
global:
|
3
|
+
- CC_TEST_REPORTER_ID=9a548b48c1c0804d8fb9083ae8e9693a5d5197eb86e8cf1b63a89d18f33e0464
|
4
|
+
sudo: false
|
5
|
+
language: ruby
|
6
|
+
rvm:
|
7
|
+
- "2.6.6"
|
8
|
+
- "2.7.1"
|
9
|
+
before_script:
|
10
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
11
|
+
- chmod +x ./cc-test-reporter
|
12
|
+
- ./cc-test-reporter before-build
|
13
|
+
script: ./bin/spec
|
14
|
+
after_script:
|
15
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Mark Burns
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
[](https://travis-ci.org/markburns/numbers_in_words)
|
2
|
+
[](https://codeclimate.com/github/markburns/numbers_in_words/maintainability)
|
3
|
+
[](https://codeclimate.com/github/markburns/numbers_in_words/test_coverage)
|
4
|
+
[](https://rubygems.org/gems/numbers_in_words)
|
5
|
+
[](http://markburns.mit-license.org)
|
6
|
+
|
7
|
+
Installation
|
8
|
+
============
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'numbers_in_words'
|
12
|
+
```
|
13
|
+
|
14
|
+
Usage
|
15
|
+
=========
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
require 'numbers_in_words'
|
19
|
+
|
20
|
+
NumbersInWords.in_words(112)
|
21
|
+
#=> one hundred and twelve
|
22
|
+
|
23
|
+
NumbersInWords.in_numbers("one googol")
|
24
|
+
#=>10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
25
|
+
|
26
|
+
NumbersInWords.in_numbers("Seventy million, five-hundred and fifty six thousand point eight nine three")
|
27
|
+
#=> 70556000.893
|
28
|
+
|
29
|
+
NumbersInWords.in_numbers("nineteen sixty five")
|
30
|
+
#=> 1965
|
31
|
+
```
|
32
|
+
|
33
|
+
|
34
|
+
Monkey patch version
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require 'numbers_in_words'
|
38
|
+
require 'numbers_in_words/duck_punch'
|
39
|
+
112.in_words
|
40
|
+
#=> one hundred and twelve
|
41
|
+
|
42
|
+
"one googol".in_numbers
|
43
|
+
#=>10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
44
|
+
|
45
|
+
"Seventy million, five-hundred and fifty six thousand point eight nine three".in_numbers
|
46
|
+
#=> 70556000.893
|
47
|
+
```
|
data/Rakefile
CHANGED
@@ -1,44 +1,3 @@
|
|
1
|
-
|
2
|
-
require 'rake'
|
3
|
-
require 'echoe'
|
4
|
-
require 'metric_fu'
|
5
|
-
|
6
|
-
Echoe.new('numbers_in_words','0.1.1') do |e|
|
7
|
-
e.description = "#in_words method for integers and #in_numbers for strings"
|
8
|
-
e.summary = "Example: 123.in_words #=> \"one hundred and twenty three\", \"seventy-five point eight\".in_numbers #=> 75.8"
|
9
|
-
e.url = "http://rubygems.org/gems/numbers_in_words"
|
10
|
-
e.author = "Mark Burns"
|
11
|
-
e.email = "markthedeveloper@gmail.com"
|
12
|
-
e.ignore_pattern = ["tmp/*",".git/*"]
|
13
|
-
e.dependencies = ['active_support']
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
|
-
MetricFu::Configuration.run do |config|
|
18
|
-
#define which metrics you want to use
|
19
|
-
config.metrics = [:churn, :saikuro, :stats, :flog, :flay, :reek, :roodi, :rcov]
|
20
|
-
config.graphs = [:flog, :flay, :reek, :roodi, :rcov]
|
21
|
-
config.flay = { :dirs_to_flay => ['lib'], :minimum_score => 100 }
|
22
|
-
config.flog = { :dirs_to_flog => [ 'lib'] }
|
23
|
-
config.reek = { :dirs_to_reek => ['lib'] }
|
24
|
-
config.roodi = { :dirs_to_roodi => ['lib'] }
|
25
|
-
config.saikuro = { :output_directory => 'scratch_directory/saikuro',
|
26
|
-
:input_directory => ['lib'],
|
27
|
-
:cyclo => "",
|
28
|
-
:filter_cyclo => "0",
|
29
|
-
:warn_cyclo => "5",
|
30
|
-
:error_cyclo => "7",
|
31
|
-
:formater => "text"} #this needs to be set to "text"
|
32
|
-
config.churn = { :start_date => "1 year ago", :minimum_churn_count => 10}
|
33
|
-
config.rcov = { :environment => 'test',
|
34
|
-
:test_files => ['spec/**/*_spec.rb'],
|
35
|
-
:rcov_opts => ["--sort coverage",
|
36
|
-
"--no-html",
|
37
|
-
"--text-coverage",
|
38
|
-
"--no-color",
|
39
|
-
"--profile",
|
40
|
-
"--rails",
|
41
|
-
"--exclude /gems/,/Library/,spec"]}
|
42
|
-
config.graph_engine = :bluff
|
43
|
-
end
|
1
|
+
# frozen_string_literal: true
|
44
2
|
|
3
|
+
require 'bundler/gem_tasks'
|
data/bin/spec
ADDED
data/lib/numbers_in_words.rb
CHANGED
@@ -1,7 +1,58 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'numbers_in_words/version'
|
4
|
+
require 'numbers_in_words/exceptional_numbers'
|
5
|
+
require 'numbers_in_words/parsing/number_parser'
|
6
|
+
require 'numbers_in_words/to_word'
|
7
|
+
require 'numbers_in_words/parsing/to_number'
|
4
8
|
|
5
|
-
|
6
|
-
|
9
|
+
module NumbersInWords
|
10
|
+
LENGTH_OF_GOOGOL = 101 # length of the string i.e. one with 100 zeros
|
11
|
+
Error = ::Class.new(::StandardError)
|
12
|
+
AmbiguousParsingError = ::Class.new(Error)
|
13
|
+
DivideByZeroError = ::Class.new(Error)
|
14
|
+
InvalidNumber = ::Class.new(Error)
|
7
15
|
|
16
|
+
class << self
|
17
|
+
extend Forwardable
|
18
|
+
def_delegators :exceptional_numbers, :fraction
|
19
|
+
|
20
|
+
def in_words(num, fraction: false)
|
21
|
+
ToWord.new(num).in_words(fraction: fraction)
|
22
|
+
end
|
23
|
+
|
24
|
+
def in_numbers(words, only_compress: false)
|
25
|
+
ToNumber.new(words, only_compress).call
|
26
|
+
end
|
27
|
+
|
28
|
+
def exceptional_numbers
|
29
|
+
@exceptional_numbers ||= ExceptionalNumbers.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def lookup(number)
|
33
|
+
exceptional_numbers.lookup(number)
|
34
|
+
end
|
35
|
+
|
36
|
+
def exceptional_number(text)
|
37
|
+
exceptional_numbers_to_i[text]
|
38
|
+
end
|
39
|
+
|
40
|
+
def power_of_ten(text)
|
41
|
+
powers_of_ten_to_i[text]
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def exceptional_numbers_to_i
|
47
|
+
@exceptional_numbers_to_i ||= swap_keys exceptional_numbers.to_h
|
48
|
+
end
|
49
|
+
|
50
|
+
def powers_of_ten_to_i
|
51
|
+
@powers_of_ten_to_i ||= swap_keys POWERS_OF_TEN
|
52
|
+
end
|
53
|
+
|
54
|
+
def swap_keys(hash)
|
55
|
+
hash.each_with_object({}) { |(k, v), h| h[v] = k }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NumbersInWords
|
4
|
+
module NumericExtension
|
5
|
+
def in_words(fraction: false)
|
6
|
+
NumbersInWords::ToWord.new(self).in_words(fraction: fraction)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module StringExtension
|
11
|
+
def in_numbers(only_compress: false)
|
12
|
+
NumbersInWords::ToNumber.new(self, only_compress).call
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class String
|
18
|
+
include NumbersInWords::StringExtension
|
19
|
+
end
|
20
|
+
|
21
|
+
class Numeric
|
22
|
+
include NumbersInWords::NumericExtension
|
23
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
require_relative 'fraction'
|
6
|
+
require_relative 'powers_of_ten'
|
7
|
+
|
8
|
+
module NumbersInWords
|
9
|
+
class ExceptionalNumbers
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
DEFINITIONS = {
|
13
|
+
0 => { number: 'zero', ordinal: 'zeroth' },
|
14
|
+
1 => { number: 'one', ordinal: 'first' },
|
15
|
+
2 => { number: 'two', ordinal: 'second', fraction: { singular: 'half', plural: 'halves' } },
|
16
|
+
3 => { number: 'three', ordinal: 'third' },
|
17
|
+
4 => { number: 'four', ordinal: 'fourth', fraction: { singular: 'quarter', plural: 'quarters' } },
|
18
|
+
5 => { number: 'five', ordinal: 'fifth' },
|
19
|
+
6 => { number: 'six' },
|
20
|
+
7 => { number: 'seven' },
|
21
|
+
8 => { number: 'eight', ordinal: 'eighth' },
|
22
|
+
9 => { number: 'nine', ordinal: 'ninth' },
|
23
|
+
10 => { number: 'ten' },
|
24
|
+
11 => { number: 'eleven' },
|
25
|
+
12 => { number: 'twelve', ordinal: 'twelfth' },
|
26
|
+
13 => { number: 'thirteen' },
|
27
|
+
14 => { number: 'fourteen' },
|
28
|
+
15 => { number: 'fifteen' },
|
29
|
+
16 => { number: 'sixteen' },
|
30
|
+
17 => { number: 'seventeen' },
|
31
|
+
18 => { number: 'eighteen' },
|
32
|
+
19 => { number: 'nineteen' },
|
33
|
+
20 => { number: 'twenty', ordinal: 'twentieth' },
|
34
|
+
30 => { number: 'thirty', ordinal: 'thirtieth' },
|
35
|
+
40 => { number: 'forty', ordinal: 'fortieth' },
|
36
|
+
50 => { number: 'fifty', ordinal: 'fiftieth' },
|
37
|
+
60 => { number: 'sixty', ordinal: 'sixtieth' },
|
38
|
+
70 => { number: 'seventy', ordinal: 'seventieth' },
|
39
|
+
80 => { number: 'eighty', ordinal: 'eightieth' },
|
40
|
+
90 => { number: 'ninety', ordinal: 'ninetieth' }
|
41
|
+
}.freeze
|
42
|
+
|
43
|
+
def fraction_names
|
44
|
+
@fraction_names ||= determine_fraction_names
|
45
|
+
end
|
46
|
+
|
47
|
+
def lookup_fraction(words)
|
48
|
+
fraction_lookup[words]
|
49
|
+
end
|
50
|
+
|
51
|
+
def fraction_lookup
|
52
|
+
@fraction_lookup ||= generate_fraction_lookup
|
53
|
+
end
|
54
|
+
|
55
|
+
def lookup(number)
|
56
|
+
to_h[number]
|
57
|
+
end
|
58
|
+
|
59
|
+
def fraction(denominator: nil, numerator: nil, word: nil)
|
60
|
+
raise unless denominator || word
|
61
|
+
|
62
|
+
numerator ||= 1
|
63
|
+
|
64
|
+
denominator ||= NumbersInWords.in_numbers(word)
|
65
|
+
|
66
|
+
Fraction.new(denominator: denominator, numerator: numerator, attributes: DEFINITIONS[denominator])
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_h
|
70
|
+
@to_h ||= DEFINITIONS.transform_values do |h|
|
71
|
+
h[:number]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def generate_fraction_lookup
|
78
|
+
named_fractions.each_with_object({}) do |f, result|
|
79
|
+
f.lookup_keys.each do |k|
|
80
|
+
key = k.split(' ').last
|
81
|
+
result[key] = 1.0 / f.denominator.to_f
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def named_fractions
|
87
|
+
@named_fractions ||= numbers.flat_map do |n|
|
88
|
+
[
|
89
|
+
Fraction.new(denominator: n, numerator: 1),
|
90
|
+
Fraction.new(denominator: n, numerator: 2)
|
91
|
+
]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def numbers
|
96
|
+
(2..100).to_a + powers_of_ten_skipping_googolplex.map { |p| 10**p }
|
97
|
+
end
|
98
|
+
|
99
|
+
def powers_of_ten_skipping_googolplex
|
100
|
+
POWERS_OF_TEN.keys[0..-2]
|
101
|
+
end
|
102
|
+
|
103
|
+
def determine_fraction_names
|
104
|
+
names = named_fractions.map(&:in_words)
|
105
|
+
|
106
|
+
words = names.map(&:split).map(&:last)
|
107
|
+
words += strip_punctuation(words)
|
108
|
+
words.uniq
|
109
|
+
end
|
110
|
+
|
111
|
+
def strip_punctuation(words)
|
112
|
+
words.map { |w| w.gsub(/^a-z/, ' ') }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|