eye-of-newt 0.0.2 → 0.0.3
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 +5 -1
- data/eye_of_newt.gemspec +2 -1
- data/lib/eye_of_newt.rb +16 -17
- data/lib/eye_of_newt/errors.rb +23 -0
- data/lib/eye_of_newt/ingredient.rb +6 -4
- data/lib/eye_of_newt/parser.rb +15 -10
- data/lib/eye_of_newt/parser.y +15 -10
- data/lib/eye_of_newt/quantity.rb +48 -0
- data/lib/eye_of_newt/tokenizer.rb +6 -4
- data/lib/eye_of_newt/units.rb +116 -0
- data/lib/eye_of_newt/version.rb +1 -1
- data/test/examples.txt +1 -1
- data/test/eye_of_newt/quantity_test.rb +29 -0
- data/test/eye_of_newt/tokenizer_test.rb +1 -2
- data/test/eye_of_newt/units_test.rb +73 -0
- data/test/eye_of_newt_test.rb +5 -5
- data/test/test_helper.rb +1 -0
- metadata +22 -16
- data/lib/eye_of_newt/unit.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9030bbeaacdf2439015bac1867eb22af36900b96
|
4
|
+
data.tar.gz: 50f0e11a38c624b45d698188f85f78d10fd79d02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25eab353b59c33bfc79143032111833f2917941f715f4e85c60680e46b44ce71ac06144346d8e8a2c5bbf7415fe684e54560bbcbe2eee279510a8ae340ca8b1f
|
7
|
+
data.tar.gz: f2c42a2b47411636fc8a91cea55bdfdc6bc0713fc1f57324ab32b342b3ef091224927c36fa6412d922d2c4e351c898cb6a85f542f59599daae726aab60d95d9c
|
data/README.md
CHANGED
@@ -22,11 +22,15 @@ Or install it yourself as:
|
|
22
22
|
```
|
23
23
|
ingredient = EyeOfNewt.parse("1 1/2 cups white flour, sifted")
|
24
24
|
ingredient.name # == 'white flour'
|
25
|
-
ingredient.
|
25
|
+
ingredient.amount # == 1.5
|
26
26
|
ingredient.unit # == 'cups'
|
27
27
|
ingredient.style # == 'sifted'
|
28
28
|
```
|
29
29
|
|
30
|
+
## Acknowledgements
|
31
|
+
|
32
|
+
The original list of units was taken from the [ingreedy project](https://github.com/iancanderson/ingreedy/blob/34d83a7f345efd1522065ac57f5ff0e9735e57de/lib/ingreedy/ingreedy_parser.rb#L59) by Ian C. Anderson
|
33
|
+
|
30
34
|
## Contributing
|
31
35
|
|
32
36
|
1. Fork it ( http://github.com/<my-github-username>/eye_of_newt/fork )
|
data/eye_of_newt.gemspec
CHANGED
@@ -18,10 +18,11 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.add_dependency "activesupport"
|
22
|
+
|
21
23
|
spec.add_development_dependency "bundler", "~> 1.5"
|
22
24
|
spec.add_development_dependency "rake"
|
23
25
|
spec.add_development_dependency "racc"
|
24
|
-
spec.add_development_dependency "activesupport"
|
25
26
|
spec.add_development_dependency "pry"
|
26
27
|
spec.add_development_dependency "pry-byebug"
|
27
28
|
spec.add_development_dependency "pry-rescue"
|
data/lib/eye_of_newt.rb
CHANGED
@@ -1,24 +1,23 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
require "eye_of_newt/tokenizer"
|
4
|
-
require "eye_of_newt/parser"
|
5
|
-
require "eye_of_newt/ingredient"
|
1
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
6
2
|
|
7
3
|
module EyeOfNewt
|
8
|
-
class InvalidIngredient < StandardError
|
9
|
-
attr_accessor :original
|
10
|
-
def initialize(line, original=nil)
|
11
|
-
super(%Q{Could not parse "#{line}" as ingredient})
|
12
|
-
self.original = original
|
13
|
-
end
|
14
|
-
end
|
15
4
|
|
16
|
-
|
17
|
-
|
18
|
-
|
5
|
+
require "eye_of_newt/version"
|
6
|
+
|
7
|
+
require "eye_of_newt/tokenizer"
|
8
|
+
require "eye_of_newt/parser"
|
9
|
+
require "eye_of_newt/ingredient"
|
10
|
+
require "eye_of_newt/units"
|
11
|
+
require "eye_of_newt/quantity"
|
12
|
+
require "eye_of_newt/errors"
|
13
|
+
|
14
|
+
mattr_accessor :units
|
15
|
+
self.units = Units.defaults
|
16
|
+
|
17
|
+
def self.parse(ingredient_line, units: self.units)
|
18
|
+
tokenizer = Tokenizer.new(ingredient_line)
|
19
|
+
parser = Parser.new(tokenizer, units: units)
|
19
20
|
parser.parse
|
20
|
-
rescue Racc::ParseError
|
21
|
-
raise InvalidIngredient, ingredient_line
|
22
21
|
end
|
23
22
|
|
24
23
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module EyeOfNewt
|
2
|
+
class EyeOfNewtError < StandardError; end
|
3
|
+
|
4
|
+
class UnknownUnit < EyeOfNewtError
|
5
|
+
def initialize(unit)
|
6
|
+
super(%Q{Unknown unit '#{unit}'})
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class UnknownConversion < EyeOfNewtError
|
11
|
+
def initialize(from, to)
|
12
|
+
super(%Q{Can't convert from "#{from}" to "#{to}"})
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class InvalidIngredient < EyeOfNewtError
|
17
|
+
attr_accessor :original
|
18
|
+
def initialize(line, original=nil)
|
19
|
+
super(%Q{Could not parse "#{line}" as ingredient})
|
20
|
+
self.original = original
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
module EyeOfNewt
|
2
2
|
class Ingredient
|
3
|
-
attr_accessor :
|
3
|
+
attr_accessor :amount, :unit, :name, :style
|
4
4
|
|
5
|
-
def initialize
|
6
|
-
self.
|
7
|
-
self.unit =
|
5
|
+
def initialize(amount: nil, unit: nil, name: nil, style: nil)
|
6
|
+
self.amount = amount
|
7
|
+
self.unit = unit
|
8
|
+
self.name = name
|
9
|
+
self.style = style
|
8
10
|
end
|
9
11
|
end
|
10
12
|
end
|
data/lib/eye_of_newt/parser.rb
CHANGED
@@ -9,11 +9,10 @@ module EyeOfNewt
|
|
9
9
|
class Parser < Racc::Parser
|
10
10
|
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
def initialize(tokenizer, ingredient = EyeOfNewt::Ingredient.new)
|
12
|
+
def initialize(tokenizer, units:, ingredient: nil)
|
15
13
|
@tokenizer = tokenizer
|
16
|
-
@
|
14
|
+
@units = units
|
15
|
+
@ingredient = ingredient || default_ingredient
|
17
16
|
super()
|
18
17
|
end
|
19
18
|
|
@@ -24,10 +23,16 @@ module EyeOfNewt
|
|
24
23
|
def parse
|
25
24
|
do_parse
|
26
25
|
@ingredient
|
26
|
+
rescue Racc::ParseError
|
27
|
+
raise InvalidIngredient, @tokenizer.string
|
27
28
|
end
|
28
29
|
|
29
30
|
def to_unit(u)
|
30
|
-
|
31
|
+
@units[u]
|
32
|
+
end
|
33
|
+
|
34
|
+
def default_ingredient
|
35
|
+
EyeOfNewt::Ingredient.new(amount: 1, unit: @units.default)
|
31
36
|
end
|
32
37
|
##### State transition tables begin ###
|
33
38
|
|
@@ -188,27 +193,27 @@ end
|
|
188
193
|
# reduce 9 omitted
|
189
194
|
|
190
195
|
def _reduce_10(val, _values, result)
|
191
|
-
@ingredient.
|
196
|
+
@ingredient.amount = result
|
192
197
|
result
|
193
198
|
end
|
194
199
|
|
195
200
|
def _reduce_11(val, _values, result)
|
196
|
-
@ingredient.
|
201
|
+
@ingredient.amount = val[0] + val[1]
|
197
202
|
result
|
198
203
|
end
|
199
204
|
|
200
205
|
def _reduce_12(val, _values, result)
|
201
|
-
@ingredient.
|
206
|
+
@ingredient.amount = result
|
202
207
|
result
|
203
208
|
end
|
204
209
|
|
205
210
|
def _reduce_13(val, _values, result)
|
206
|
-
@ingredient.
|
211
|
+
@ingredient.amount = result
|
207
212
|
result
|
208
213
|
end
|
209
214
|
|
210
215
|
def _reduce_14(val, _values, result)
|
211
|
-
@ingredient.
|
216
|
+
@ingredient.amount = 1
|
212
217
|
result
|
213
218
|
end
|
214
219
|
|
data/lib/eye_of_newt/parser.y
CHANGED
@@ -17,11 +17,11 @@ rule
|
|
17
17
|
| amount
|
18
18
|
;
|
19
19
|
amount
|
20
|
-
: number { @ingredient.
|
21
|
-
| number fraction { @ingredient.
|
22
|
-
| fraction { @ingredient.
|
23
|
-
| decimal { @ingredient.
|
24
|
-
| a { @ingredient.
|
20
|
+
: number { @ingredient.amount = result }
|
21
|
+
| number fraction { @ingredient.amount = val[0] + val[1] }
|
22
|
+
| fraction { @ingredient.amount = result }
|
23
|
+
| decimal { @ingredient.amount = result }
|
24
|
+
| a { @ingredient.amount = 1 }
|
25
25
|
;
|
26
26
|
style : ',' words { @ingredient.style = val[1] } ;
|
27
27
|
words
|
@@ -39,11 +39,10 @@ end
|
|
39
39
|
|
40
40
|
---- inner
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
def initialize(tokenizer, ingredient = EyeOfNewt::Ingredient.new)
|
42
|
+
def initialize(tokenizer, units:, ingredient: nil)
|
45
43
|
@tokenizer = tokenizer
|
46
|
-
@
|
44
|
+
@units = units
|
45
|
+
@ingredient = ingredient || default_ingredient
|
47
46
|
super()
|
48
47
|
end
|
49
48
|
|
@@ -54,8 +53,14 @@ end
|
|
54
53
|
def parse
|
55
54
|
do_parse
|
56
55
|
@ingredient
|
56
|
+
rescue Racc::ParseError
|
57
|
+
raise InvalidIngredient, @tokenizer.string
|
57
58
|
end
|
58
59
|
|
59
60
|
def to_unit(u)
|
60
|
-
|
61
|
+
@units[u]
|
62
|
+
end
|
63
|
+
|
64
|
+
def default_ingredient
|
65
|
+
EyeOfNewt::Ingredient.new(amount: 1, unit: @units.default)
|
61
66
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
|
3
|
+
module EyeOfNewt
|
4
|
+
class Quantity
|
5
|
+
DELTA = 0.01
|
6
|
+
SIGNIFICANT_DIGITS = 3
|
7
|
+
|
8
|
+
attr_reader :amount, :unit, :units
|
9
|
+
|
10
|
+
def initialize(amount, unit, units: EyeOfNewt.units)
|
11
|
+
@amount = amount
|
12
|
+
@units = units
|
13
|
+
@unit = units[unit]
|
14
|
+
end
|
15
|
+
|
16
|
+
def in(new_unit)
|
17
|
+
rate = units.conversion_rate(unit, new_unit)
|
18
|
+
self.class.new(amount * rate, new_unit, units: units)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
[fraction_str, unit_str].compact.join(' ')
|
23
|
+
end
|
24
|
+
alias :inspect :to_s
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def fraction_str
|
29
|
+
whole = fraction.to_i
|
30
|
+
fractional = fraction - whole
|
31
|
+
[whole, fractional].reject(&:zero?).join(' ')
|
32
|
+
end
|
33
|
+
|
34
|
+
def unit_str
|
35
|
+
return nil if unit == units.default
|
36
|
+
singular = fraction <= 1 && fraction.numerator == 1
|
37
|
+
singular ? unit.singularize : unit
|
38
|
+
end
|
39
|
+
|
40
|
+
def fraction
|
41
|
+
@fraction ||= signif(amount, SIGNIFICANT_DIGITS).to_r.rationalize(DELTA)
|
42
|
+
end
|
43
|
+
|
44
|
+
def signif(value, digits)
|
45
|
+
Float("%.#{digits}g" % value)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'strscan'
|
2
|
-
require 'eye_of_newt/unit'
|
3
2
|
|
4
3
|
module EyeOfNewt
|
5
4
|
class Tokenizer
|
@@ -11,7 +10,10 @@ module EyeOfNewt
|
|
11
10
|
OF = /of/
|
12
11
|
A = /an?/
|
13
12
|
|
14
|
-
|
13
|
+
attr_reader :string, :units
|
14
|
+
|
15
|
+
def initialize(string, units: EyeOfNewt.units.all)
|
16
|
+
@string = string
|
15
17
|
@units = units
|
16
18
|
@ss = StringScanner.new(string)
|
17
19
|
end
|
@@ -41,8 +43,8 @@ module EyeOfNewt
|
|
41
43
|
private
|
42
44
|
|
43
45
|
def unit_matcher
|
44
|
-
@unit_matcher ||= if
|
45
|
-
r =
|
46
|
+
@unit_matcher ||= if units.any?
|
47
|
+
r = units
|
46
48
|
.sort
|
47
49
|
.reverse
|
48
50
|
.map{|u|Regexp.escape(u)}
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'active_support/core_ext/hash/deep_merge'
|
2
|
+
|
3
|
+
module EyeOfNewt
|
4
|
+
class Units
|
5
|
+
attr_reader :units, :conversions, :default
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@units = {}
|
9
|
+
@conversions = Hash.new { |h, k| h[k] = {} }
|
10
|
+
@default = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def all
|
14
|
+
units.keys
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](unit)
|
18
|
+
units[unit] or raise UnknownUnit.new(unit)
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_unit(canonical, *variations, default: false)
|
22
|
+
units[canonical] = canonical
|
23
|
+
variations.each do |v|
|
24
|
+
units[v] = canonical
|
25
|
+
end
|
26
|
+
|
27
|
+
@default = canonical if default
|
28
|
+
|
29
|
+
conversions[canonical][canonical] = 1
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_conversion(amount, unit, other_unit)
|
33
|
+
unit = self[unit]
|
34
|
+
other_unit = self[other_unit]
|
35
|
+
|
36
|
+
new_conversion = {unit => {other_unit => amount.to_r}, other_unit => {unit => 1/amount.to_r}}
|
37
|
+
conversions.deep_merge!(new_conversion)
|
38
|
+
end
|
39
|
+
|
40
|
+
def conversion_rate(from, to)
|
41
|
+
f = self[from]
|
42
|
+
t = self[to]
|
43
|
+
r = search_conversion(f, t) or raise UnknownConversion.new(from, to)
|
44
|
+
r.to_f
|
45
|
+
end
|
46
|
+
|
47
|
+
def setup(&block)
|
48
|
+
instance_eval &block
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.defaults
|
53
|
+
new.setup do
|
54
|
+
# english volume units
|
55
|
+
add_unit "cups", "c.", "c", "cup"
|
56
|
+
add_unit "fl oz", "fl. oz.", "fluid ounces", "fluid ounce"
|
57
|
+
add_unit "gallons", "gal", "gal.", "gallon"
|
58
|
+
add_unit "pints", "pt", "pt.", "pint"
|
59
|
+
add_unit "quarts", "qt", "qt.", "qts", "qts.", "quart"
|
60
|
+
add_unit "tbsp", "tbsp.", "T", "T.", "tbs.", "tbs", "tablespoons", "tablespoon"
|
61
|
+
add_unit "tsp", "tsp.", "t", "t.", "teaspoons", "teaspoon"
|
62
|
+
|
63
|
+
# english mass units
|
64
|
+
add_unit "lb", "lb.", "pounds", "pound"
|
65
|
+
add_unit "oz", "oz.", "ounces", "ounce"
|
66
|
+
|
67
|
+
# metric volume units
|
68
|
+
add_unit "l", "l.", "liter", "liters", "litre", "litres"
|
69
|
+
add_unit "ml", "ml.", "milliliter", "milliliters", "millilitre", "millilitres"
|
70
|
+
|
71
|
+
# metric mass units
|
72
|
+
add_unit "kg", "kg.", "kilogram", "kilograms"
|
73
|
+
add_unit "g", "g.", "gr", "gr.", "gram", "grams"
|
74
|
+
add_unit "mg", "mg", "mg.", "milligram", "milligrams"
|
75
|
+
|
76
|
+
# nonstandard units
|
77
|
+
add_unit "pinches", "pinch"
|
78
|
+
add_unit "dashes", "dash"
|
79
|
+
add_unit "touches", "touch"
|
80
|
+
add_unit "handfuls", "handful"
|
81
|
+
|
82
|
+
add_unit "units", "unit", default: true
|
83
|
+
|
84
|
+
add_conversion 16, "tbsp", "cup"
|
85
|
+
add_conversion 8, "fl oz", "cup"
|
86
|
+
add_conversion 235, "ml", "cup"
|
87
|
+
add_conversion 4, "quarts", "gallon"
|
88
|
+
add_conversion 2, "cups", "pint"
|
89
|
+
add_conversion 2, "pints", "quart"
|
90
|
+
add_conversion 3, "tsp", "tbsp"
|
91
|
+
|
92
|
+
add_conversion 16, "oz", "pound"
|
93
|
+
add_conversion 454, "grams", "pound"
|
94
|
+
|
95
|
+
add_conversion 1000, "ml", "liter"
|
96
|
+
|
97
|
+
add_conversion 1000, "g", "kg"
|
98
|
+
add_conversion 1000, "mg", "g"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def search_conversion(from, to, rate=1, visited=[])
|
105
|
+
return rate if from == to
|
106
|
+
visited = visited + [from]
|
107
|
+
conversions[from].each do |k, r|
|
108
|
+
next if visited.include?(k)
|
109
|
+
value = search_conversion(k, to, rate*r, visited)
|
110
|
+
return value if value
|
111
|
+
end
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
data/lib/eye_of_newt/version.rb
CHANGED
data/test/examples.txt
CHANGED
@@ -10,7 +10,7 @@ tomatoes | tomatoes | 1 | units
|
|
10
10
|
1 tomato, diced | tomato | 1 | units | diced
|
11
11
|
1 onion, finely chopped | onion | 1 | units | finely chopped
|
12
12
|
1 cup of flour | flour | 1 | cups
|
13
|
-
1 teaspoon of cream of tartar | cream of tartar | 1 |
|
13
|
+
1 teaspoon of cream of tartar | cream of tartar | 1 | tsp
|
14
14
|
a pinch of salt | salt | 1 | pinches
|
15
15
|
|
16
16
|
#1 (46 fluid ounce) can tomato juice' => ["1 (46 fluid ounce) can", "tomato juice"],
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EyeOfNewt::QuantityTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
def units
|
6
|
+
EyeOfNewt::Units.new.setup do
|
7
|
+
add_unit "foo"
|
8
|
+
add_unit "bar"
|
9
|
+
add_conversion 2, "foo", "bar"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
test "#in returns a quantity in the new unit" do
|
14
|
+
bar = EyeOfNewt::Quantity.new(1, "bar", units: units)
|
15
|
+
foo = bar.in("foo")
|
16
|
+
assert_equal 0.5, foo.amount
|
17
|
+
assert_equal "foo", foo.unit
|
18
|
+
end
|
19
|
+
|
20
|
+
test "#to_s makes appropriate strings" do
|
21
|
+
assert_equal "1", EyeOfNewt::Quantity.new(1, "unit").to_s
|
22
|
+
assert_equal "1/2", EyeOfNewt::Quantity.new(0.5, "units").to_s
|
23
|
+
assert_equal "1/2 tbsp", EyeOfNewt::Quantity.new(0.5, "tbs").to_s
|
24
|
+
assert_equal "1 1/2 tbsp", EyeOfNewt::Quantity.new(1.5, "tbs").to_s
|
25
|
+
assert_equal "1000 g", EyeOfNewt::Quantity.new(1001, "grams").to_s
|
26
|
+
assert_equal "1/3 g", EyeOfNewt::Quantity.new(0.33, "grams").to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'test_helper'
|
2
|
-
require 'eye_of_newt/tokenizer'
|
3
2
|
|
4
3
|
class EyeOfNewt::TokenizerTest < ActiveSupport::TestCase
|
5
4
|
test "tokenizes WORD" do
|
@@ -51,6 +50,6 @@ class EyeOfNewt::TokenizerTest < ActiveSupport::TestCase
|
|
51
50
|
end
|
52
51
|
|
53
52
|
def tok(string, units=[])
|
54
|
-
EyeOfNewt::Tokenizer.new(string, units)
|
53
|
+
EyeOfNewt::Tokenizer.new(string, units: units)
|
55
54
|
end
|
56
55
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EyeOfNewt::UnitsTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
test "#all returns the list of unit names" do
|
6
|
+
units = EyeOfNewt::Units.new.setup do
|
7
|
+
add_unit "foo", "f"
|
8
|
+
add_unit "bar", "b"
|
9
|
+
end
|
10
|
+
assert_equal ["foo", "f", "bar", "b"], units.all
|
11
|
+
end
|
12
|
+
|
13
|
+
test "#default returns the default unit" do
|
14
|
+
units = EyeOfNewt::Units.new.setup do
|
15
|
+
add_unit "foo", "f", default: true
|
16
|
+
end
|
17
|
+
assert_equal "foo", units.default
|
18
|
+
end
|
19
|
+
|
20
|
+
test "#[] returns the canonical name of the unit" do
|
21
|
+
units = EyeOfNewt::Units.new.setup do
|
22
|
+
add_unit "foo", "f"
|
23
|
+
end
|
24
|
+
assert_equal "foo", units["f"]
|
25
|
+
assert_equal "foo", units["foo"]
|
26
|
+
end
|
27
|
+
|
28
|
+
test "#[] raises an exception for unknown units" do
|
29
|
+
units = EyeOfNewt::Units.new
|
30
|
+
assert_raise EyeOfNewt::UnknownUnit do
|
31
|
+
units["foo"]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
test "#conversion_rate throws an error for unknown conversions" do
|
36
|
+
units = EyeOfNewt::Units.new.setup do
|
37
|
+
add_unit "foo"
|
38
|
+
add_unit "bar"
|
39
|
+
end
|
40
|
+
assert_raise EyeOfNewt::UnknownConversion do
|
41
|
+
units.conversion_rate("foo", "bar")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
test "#conversion_rate succeeds for self-conversions" do
|
46
|
+
units = EyeOfNewt::Units.new.setup do
|
47
|
+
add_unit "foo"
|
48
|
+
end
|
49
|
+
assert_equal 1, units.conversion_rate("foo", "foo")
|
50
|
+
end
|
51
|
+
|
52
|
+
test "#conversion_rate returns the conversion rate" do
|
53
|
+
units = EyeOfNewt::Units.new.setup do
|
54
|
+
add_unit "foo"
|
55
|
+
add_unit "bar"
|
56
|
+
add_conversion 2, "foo", "bar"
|
57
|
+
end
|
58
|
+
assert_equal 2, units.conversion_rate("foo", "bar")
|
59
|
+
assert_equal 0.5, units.conversion_rate("bar", "foo")
|
60
|
+
end
|
61
|
+
|
62
|
+
test "#conversion_rate can find indirect conversions" do
|
63
|
+
units = EyeOfNewt::Units.new.setup do
|
64
|
+
add_unit "foo"
|
65
|
+
add_unit "bar"
|
66
|
+
add_unit "baz"
|
67
|
+
add_conversion 2, "foo", "bar"
|
68
|
+
add_conversion 2, "bar", "baz"
|
69
|
+
end
|
70
|
+
assert_equal 4, units.conversion_rate("foo", "baz")
|
71
|
+
assert_equal 0.25, units.conversion_rate("baz", "foo")
|
72
|
+
end
|
73
|
+
end
|
data/test/eye_of_newt_test.rb
CHANGED
@@ -12,10 +12,10 @@ class EyeOfNewtTest < ActiveSupport::TestCase
|
|
12
12
|
tokens = line.split('|').map(&:strip)
|
13
13
|
line = tokens.shift
|
14
14
|
name = tokens.shift
|
15
|
-
|
16
|
-
unit = tokens.shift.presence
|
15
|
+
amount = tokens.shift.to_f
|
16
|
+
unit = tokens.shift.presence
|
17
17
|
style = tokens.shift.presence
|
18
|
-
expected = [name,
|
18
|
+
expected = [name, amount, unit, style]
|
19
19
|
[line, expected]
|
20
20
|
}.compact
|
21
21
|
end
|
@@ -24,8 +24,8 @@ class EyeOfNewtTest < ActiveSupport::TestCase
|
|
24
24
|
examples.each do |line, expected|
|
25
25
|
test "parses #{line} correctly" do
|
26
26
|
ingr = EyeOfNewt.parse(line)
|
27
|
-
name,
|
28
|
-
assert_equal
|
27
|
+
name, amount, unit, style = *expected
|
28
|
+
assert_equal amount, ingr.amount, %Q{incorrect amount}
|
29
29
|
assert_equal unit, ingr.unit, %Q{incorrect unit}
|
30
30
|
assert_equal name, ingr.name, %Q{incorrect name}
|
31
31
|
assert_equal style, ingr.style, %Q{incorrect style}
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,45 +1,45 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eye-of-newt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter McCracken
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
type: :
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '1.5'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '1.5'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: racc
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -125,15 +125,19 @@ files:
|
|
125
125
|
- eye_of_newt.gemspec
|
126
126
|
- lib/eye-of-newt.rb
|
127
127
|
- lib/eye_of_newt.rb
|
128
|
+
- lib/eye_of_newt/errors.rb
|
128
129
|
- lib/eye_of_newt/ingredient.rb
|
129
130
|
- lib/eye_of_newt/parser.rb
|
130
131
|
- lib/eye_of_newt/parser.y
|
132
|
+
- lib/eye_of_newt/quantity.rb
|
131
133
|
- lib/eye_of_newt/tokenizer.rb
|
132
|
-
- lib/eye_of_newt/
|
134
|
+
- lib/eye_of_newt/units.rb
|
133
135
|
- lib/eye_of_newt/version.rb
|
134
136
|
- script/console
|
135
137
|
- test/examples.txt
|
138
|
+
- test/eye_of_newt/quantity_test.rb
|
136
139
|
- test/eye_of_newt/tokenizer_test.rb
|
140
|
+
- test/eye_of_newt/units_test.rb
|
137
141
|
- test/eye_of_newt_test.rb
|
138
142
|
- test/test_helper.rb
|
139
143
|
homepage: http://github.com/peterjm/eye_of_newt
|
@@ -162,6 +166,8 @@ specification_version: 4
|
|
162
166
|
summary: Natural language ingredient parser
|
163
167
|
test_files:
|
164
168
|
- test/examples.txt
|
169
|
+
- test/eye_of_newt/quantity_test.rb
|
165
170
|
- test/eye_of_newt/tokenizer_test.rb
|
171
|
+
- test/eye_of_newt/units_test.rb
|
166
172
|
- test/eye_of_newt_test.rb
|
167
173
|
- test/test_helper.rb
|
data/lib/eye_of_newt/unit.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
# List of units was taken from the ingreedy project by Ian C. Anderson
|
2
|
-
# https://github.com/iancanderson/ingreedy/blob/34d83a7f345efd1522065ac57f5ff0e9735e57de/lib/ingreedy/ingreedy_parser.rb#L59
|
3
|
-
|
4
|
-
module EyeOfNewt
|
5
|
-
class Unit
|
6
|
-
DEFAULT = :units
|
7
|
-
|
8
|
-
class << self
|
9
|
-
def units
|
10
|
-
@units ||= {}
|
11
|
-
end
|
12
|
-
|
13
|
-
def all
|
14
|
-
units.keys
|
15
|
-
end
|
16
|
-
|
17
|
-
def canonical(unit)
|
18
|
-
units[unit]
|
19
|
-
end
|
20
|
-
alias :[] :canonical
|
21
|
-
|
22
|
-
def set(canonical, *variations)
|
23
|
-
variations.each do |v|
|
24
|
-
units[v] = canonical.to_sym
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# english units
|
30
|
-
set :cups, "c.", "c", "cup", "cups"
|
31
|
-
set :fluid_ounces, "fl. oz.", "fl oz", "fluid ounce", "fluid ounces"
|
32
|
-
set :gallons, "gal", "gal.", "gallon", "gallons"
|
33
|
-
set :ounces, "oz", "oz.", "ounce", "ounces"
|
34
|
-
set :pints, "pt", "pt.", "pint", "pints"
|
35
|
-
set :pounds, "lb", "lb.", "pound", "pounds"
|
36
|
-
set :quarts, "qt", "qt.", "qts", "qts.", "quart", "quarts"
|
37
|
-
set :tablespoons, "tbsp.", "tbsp", "T", "T.", "tablespoon", "tablespoons", "tbs.", "tbs"
|
38
|
-
set :teaspoons, "tsp.", "tsp", "t", "t.", "teaspoon", "teaspoons"
|
39
|
-
|
40
|
-
# metric units
|
41
|
-
set :grams, "g", "g.", "gr", "gr.", "gram", "grams"
|
42
|
-
set :kilograms, "kg", "kg.", "kilogram", "kilograms"
|
43
|
-
set :liters, "l", "l.", "liter", "liters", "litre", "litres"
|
44
|
-
set :milligrams, "mg", "mg.", "milligram", "milligrams"
|
45
|
-
set :milliliters, "ml", "ml.", "milliliter", "milliliters", "millilitre", "millilitres"
|
46
|
-
|
47
|
-
# nonstandard units
|
48
|
-
set :pinches, "pinch", "pinches"
|
49
|
-
set :dashes, "dash", "dashes"
|
50
|
-
set :touches, "touch", "touches"
|
51
|
-
set :handfuls, "handful", "handfuls"
|
52
|
-
set :units, "units", "unit"
|
53
|
-
end
|
54
|
-
end
|