numerouno 0.0.1

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.
data/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ == 0.0.1 2009-05-07
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
5
+ * mmmm...has that new gem smell
data/Manifest.txt ADDED
@@ -0,0 +1,13 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ lib/numerouno.rb
6
+ lib/numerouno-parsing.rb
7
+ lib/numerouno/combiner.rb
8
+ lib/numerouno/parser.rb
9
+ lib/numerouno/search.rb
10
+ script/console
11
+ script/destroy
12
+ script/generate
13
+ tasks/rspec.rake
data/README.rdoc ADDED
@@ -0,0 +1,72 @@
1
+ = Numerouno
2
+
3
+ * http://github.com/brentsnook/numerouno
4
+
5
+ English natural language parser for numbers.
6
+
7
+ * Parse 'five' to return 5
8
+ * Parse 'seven hundred and fifty two billion, four hundred and twenty million, sixty thousand and forty two' to return 752,420,060,042
9
+ * Parse 'siebenundzwanzig' to have it fail because it doesn't recognise German.
10
+
11
+ Recognises numbers in the trillions.
12
+
13
+ == Installation
14
+
15
+ Whoah, hang on there. Not up on Rubyforge yet but hopefully soon. When it is:
16
+
17
+ sudo gem install numerouno
18
+
19
+ === Building the Gem yourself
20
+
21
+ Grab the code from github:
22
+
23
+ git clone git://github.com/brentsnook/numerouno.git
24
+ cd numerouno
25
+ rake install_gem
26
+
27
+ == Use
28
+
29
+ Just require numerouno to add magical number parsing powers to your strings:
30
+
31
+ require 'numerouno'
32
+ 'sixty five'.as_number
33
+
34
+ Or if you're a sook who doesn't like the idea of String being opened up:
35
+
36
+ require 'numerouno-parsing'
37
+ Numerouno.parse 'sixty five'
38
+
39
+ == How does it all work?
40
+
41
+ The English language has explicit rules for expressing numbers. Numeruno attempts to recognise strings based on these rules.
42
+
43
+ Check out http://wiki.github.com/brentsnook/numerouno for details.
44
+
45
+ == What is it good for?
46
+
47
+ The main serving suggestion is to toss it with Chronic[http://chronic.rubyforge.org/] and Cucumber[http://cukes.info/] for an even tastier BDD salad.
48
+
49
+ == License
50
+
51
+ (The MIT License)
52
+
53
+ Copyright (c) 2009 Brent Snook http://fuglylogic.com
54
+
55
+ Permission is hereby granted, free of charge, to any person obtaining
56
+ a copy of this software and associated documentation files (the
57
+ 'Software'), to deal in the Software without restriction, including
58
+ without limitation the rights to use, copy, modify, merge, publish,
59
+ distribute, sublicense, and/or sell copies of the Software, and to
60
+ permit persons to whom the Software is furnished to do so, subject to
61
+ the following conditions:
62
+
63
+ The above copyright notice and this permission notice shall be
64
+ included in all copies or substantial portions of the Software.
65
+
66
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
67
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
68
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
69
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
70
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
71
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
72
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/numerouno'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('numerouno', Numerouno::VERSION) do |p|
7
+ p.developer 'Brent Snook', 'brent@fuglylogic.com'
8
+ p.summary = %q{English natural language parser for numbers.}
9
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
10
+ p.rubyforge_name = p.name
11
+ p.extra_dev_deps = [
12
+ ['newgem', ">= #{::Newgem::VERSION}"],
13
+ ['rspec', '>= 1.2.4'],
14
+ ['cucumber', '>= 0.3.1']
15
+ ]
16
+
17
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
18
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
19
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
20
+ p.rsync_args = '-av --delete --ignore-errors'
21
+ end
22
+
23
+ require 'newgem/tasks' # load /tasks/*.rake
24
+ Dir['tasks/**/*.rake'].each { |t| load t }
25
+
26
+ task :default => [:spec, :features]
@@ -0,0 +1,65 @@
1
+ module Numerouno
2
+
3
+ module CombinerMethods
4
+
5
+ def combine numbers
6
+ Combiner.new numbers
7
+ end
8
+
9
+ end
10
+
11
+ class Combiner
12
+
13
+ def of_power power
14
+ @power = power
15
+ self
16
+ end
17
+
18
+ def apply!
19
+ @numbers.each_with_index do |number, index|
20
+ @current = index
21
+ if number_is_correct_power?
22
+ apply :*, to_left if should_multiply?
23
+ apply :+, to_right if should_add?
24
+ end
25
+ end
26
+
27
+ @numbers.compact!
28
+ end
29
+
30
+ private
31
+
32
+ def initialize numbers
33
+ @numbers = numbers
34
+ end
35
+
36
+ def should_multiply?
37
+ @current > 0 and @power > 10
38
+ end
39
+
40
+ def should_add?
41
+ @current < (@numbers.length - 1) and number > @numbers[to_right]
42
+ end
43
+
44
+ def apply operation, index
45
+ @numbers[@current] = number.send operation, @numbers[index]
46
+ @numbers[index] = nil
47
+ end
48
+
49
+ def to_left
50
+ @current - 1
51
+ end
52
+
53
+ def to_right
54
+ @current + 1
55
+ end
56
+
57
+ def number_is_correct_power?
58
+ number and (number % @power == 0) and number < (@power * 10)
59
+ end
60
+
61
+ def number
62
+ @numbers[@current]
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,25 @@
1
+ module Numerouno
2
+ module Parser
3
+
4
+ extend CombinerMethods
5
+ extend SearchMethods
6
+
7
+ def self.number_from string
8
+ numbers = numbers_within string
9
+ numbers.empty? ? raise(NoNumberFoundError, "No number found in string: #{string}") :
10
+ total(numbers)
11
+ end
12
+
13
+ private
14
+
15
+ def self.total numbers
16
+ [10, 100, 1000, 1000000, 1000000000, 1000000000000].each do |power|
17
+ combine(numbers).of_power(power).apply!
18
+ end
19
+ numbers.inject(0){|sum, add| sum + add}
20
+ end
21
+
22
+ end
23
+
24
+ class NoNumberFoundError < StandardError; end
25
+ end
@@ -0,0 +1,94 @@
1
+ module Numerouno
2
+
3
+ module SearchMethods
4
+
5
+ def numbers_within string
6
+ Search.new(string).find_all
7
+ end
8
+
9
+ end
10
+
11
+ class Search
12
+
13
+ # the mapping between number strings and numbers needs to be ordered
14
+ # this is to avoid the wrong mapping being picked up
15
+ # i.e /seven/ would match 'seventy' before /seventy/
16
+ NUMBER_STRINGS = [
17
+ ['sixty', 60],
18
+ ['seventy', 70],
19
+ ['eighty', 80],
20
+ ['ninety', 90],
21
+ ['fourteen', 14],
22
+ ['sixteen', 16],
23
+ ['seventeen', 17],
24
+ ['eighteen', 18],
25
+ ['nineteen', 19],
26
+
27
+ ['zero', 0],
28
+ ['one', 1],
29
+ ['two', 2],
30
+ ['three', 3],
31
+ ['four', 4],
32
+ ['five', 5],
33
+ ['six', 6],
34
+ ['seven', 7],
35
+ ['eight', 8],
36
+ ['nine', 9],
37
+ ['ten', 10],
38
+ ['eleven', 11],
39
+ ['twelve', 12],
40
+ ['thirteen', 13],
41
+ ['fifteen', 15],
42
+ ['twenty', 20],
43
+ ['thirty', 30],
44
+ ['forty', 40],
45
+ ['fifty', 50],
46
+
47
+ ['hundred', 100],
48
+ ['thousand', 1000],
49
+ ['million', 1000000],
50
+ ['billion', 1000000000],
51
+ ['trillion', 1000000000000]
52
+ ]
53
+
54
+ NUMBER_LOOKUP = NUMBER_STRINGS.inject(Hash.new) do |hash, map|
55
+ hash[map[0]] = map[1]
56
+ hash
57
+ end
58
+
59
+ def initialize string
60
+ @string = string
61
+ @numbers = []
62
+ end
63
+
64
+ def find_all
65
+ search until string_exhausted?
66
+ @numbers.compact
67
+ end
68
+
69
+ private
70
+
71
+ def search
72
+ if match = next_match
73
+ @string = string_without match
74
+ @numbers[match.pre_match.length] = NUMBER_LOOKUP[match.to_s]
75
+ else
76
+ @string = ''
77
+ end
78
+ end
79
+
80
+ def string_without match
81
+ match.pre_match + (' ' * match.to_s.length) + match.post_match
82
+ end
83
+
84
+ def next_match
85
+ NUMBER_STRINGS.collect do |number_string, number|
86
+ @string.match number_string
87
+ end.compact.first
88
+ end
89
+
90
+ def string_exhausted?
91
+ @string.sub(' ', '').empty?
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,16 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ [
5
+ 'combiner',
6
+ 'search',
7
+ 'parser'
8
+ ].each {|file| require "numerouno/#{file}"}
9
+
10
+ module Numerouno
11
+ VERSION = '0.0.1'
12
+
13
+ def self.parse string
14
+ Parser.number_from string
15
+ end
16
+ end
data/lib/numerouno.rb ADDED
@@ -0,0 +1,14 @@
1
+ require File.join(File.dirname(__FILE__), 'numerouno-parsing')
2
+
3
+ module Numerouno
4
+
5
+ module StringExtensions
6
+
7
+ def as_number
8
+ Numerouno.parse self
9
+ end
10
+ end
11
+
12
+ end
13
+
14
+ String.send :include, Numerouno::StringExtensions
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/numerouno.rb'}"
9
+ puts "Loading numerouno gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/tasks/rspec.rake ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'spec'
6
+ end
7
+ begin
8
+ require 'spec/rake/spectask'
9
+ rescue LoadError
10
+ puts <<-EOS
11
+ To use rspec for testing you must install rspec gem:
12
+ gem install rspec
13
+ EOS
14
+ exit(0)
15
+ end
16
+
17
+ desc "Run the specs under spec/models"
18
+ Spec::Rake::SpecTask.new do |t|
19
+ t.spec_opts = ['--options', "spec/spec.opts"]
20
+ t.spec_files = FileList['spec/**/*_spec.rb']
21
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: numerouno
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Brent Snook
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-09 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: newgem
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.4
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: cucumber
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.3.1
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: hoe
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.8.0
54
+ version:
55
+ description: ""
56
+ email:
57
+ - brent@fuglylogic.com
58
+ executables: []
59
+
60
+ extensions: []
61
+
62
+ extra_rdoc_files:
63
+ - History.txt
64
+ - Manifest.txt
65
+ - README.rdoc
66
+ files:
67
+ - History.txt
68
+ - Manifest.txt
69
+ - README.rdoc
70
+ - Rakefile
71
+ - lib/numerouno.rb
72
+ - lib/numerouno-parsing.rb
73
+ - lib/numerouno/combiner.rb
74
+ - lib/numerouno/parser.rb
75
+ - lib/numerouno/search.rb
76
+ - script/console
77
+ - script/destroy
78
+ - script/generate
79
+ - tasks/rspec.rake
80
+ has_rdoc: true
81
+ homepage: http://github.com/brentsnook/numerouno
82
+ post_install_message:
83
+ rdoc_options:
84
+ - --main
85
+ - README.rdoc
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: "0"
93
+ version:
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: "0"
99
+ version:
100
+ requirements: []
101
+
102
+ rubyforge_project: numerouno
103
+ rubygems_version: 1.3.1
104
+ signing_key:
105
+ specification_version: 2
106
+ summary: English natural language parser for numbers.
107
+ test_files: []
108
+