ingreedy 0.0.7 → 0.0.8
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/.gitignore +5 -0
- data/.rspec +1 -0
- data/.rubocop.yml +601 -0
- data/.rvmrc +1 -0
- data/.travis.yml +14 -0
- data/Gemfile +3 -0
- data/README.md +46 -0
- data/Rakefile +5 -0
- data/ingreedy.gemspec +26 -0
- data/lib/ingreedy.rb +17 -13
- data/lib/ingreedy/amount_parser.rb +17 -18
- data/lib/ingreedy/case_insensitive_parser.rb +1 -3
- data/lib/ingreedy/dictionaries/en.yml +137 -0
- data/lib/ingreedy/dictionary.rb +19 -19
- data/lib/ingreedy/dictionary_collection.rb +5 -3
- data/lib/ingreedy/ingreedy_parser.rb +34 -116
- data/lib/ingreedy/rationalizer.rb +12 -10
- data/lib/ingreedy/root_parser.rb +118 -0
- data/lib/ingreedy/unit_variation_mapper.rb +12 -9
- data/lib/ingreedy/version.rb +1 -2
- data/spec/ingreedy/amount_parser_spec.rb +99 -0
- data/spec/ingreedy/rationalizer_spec.rb +56 -0
- data/spec/ingreedy/unit_variation_mapper_spec.rb +13 -0
- data/spec/ingreedy_spec.rb +378 -0
- data/spec/spec_helper.rb +34 -0
- metadata +23 -26
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create 1.9.3@ingreedy
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# Usage
|
2
|
+
|
3
|
+
```ruby
|
4
|
+
result = Ingreedy.parse('1 lb. potatoes')
|
5
|
+
print result.amount
|
6
|
+
#=> 1.0
|
7
|
+
print result.unit
|
8
|
+
#=> :pound
|
9
|
+
print result.ingredient
|
10
|
+
#=> "potatoes"
|
11
|
+
```
|
12
|
+
|
13
|
+
### I18n and custom dictionaries
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
Ingreedy.dictionaries[:fr] = {
|
17
|
+
units: { dash: ['pincée'] },
|
18
|
+
numbers: { 'une' => 1 },
|
19
|
+
prepositions: ['de']
|
20
|
+
}
|
21
|
+
|
22
|
+
Ingreedy.locale = :fr # Also automatically follows I18n.locale if available
|
23
|
+
|
24
|
+
result = Ingreedy.parse('une pincée de sucre')
|
25
|
+
print result.amount
|
26
|
+
#=> 1.0
|
27
|
+
print result.unit
|
28
|
+
#=> :dash
|
29
|
+
print result.ingredient
|
30
|
+
#=> "sucre"
|
31
|
+
```
|
32
|
+
|
33
|
+
[Live demo](http://hangryingreedytest.herokuapp.com/)
|
34
|
+
|
35
|
+
# Pieces of Flair
|
36
|
+
- [](http://badge.fury.io/rb/ingreedy)
|
37
|
+
- [](http://travis-ci.org/iancanderson/ingreedy)
|
38
|
+
- [](https://codeclimate.com/github/iancanderson/ingreedy)
|
39
|
+
- [](https://coveralls.io/r/iancanderson/ingreedy)
|
40
|
+
|
41
|
+
# Development
|
42
|
+
|
43
|
+
Run the tests:
|
44
|
+
```
|
45
|
+
rspec spec
|
46
|
+
```
|
data/Rakefile
ADDED
data/ingreedy.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path("../lib/ingreedy/version", __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "ingreedy"
|
5
|
+
s.version = Ingreedy::VERSION
|
6
|
+
s.licenses = ["MIT"]
|
7
|
+
s.authors = ["Ian C. Anderson"]
|
8
|
+
s.email = ["ian@iancanderson.com"]
|
9
|
+
|
10
|
+
s.summary = "Recipe parser"
|
11
|
+
s.description = <<-MSG
|
12
|
+
Natural language recipe ingredient parser that supports numeric amount,
|
13
|
+
units, and ingredient
|
14
|
+
MSG
|
15
|
+
s.homepage = "http://github.com/iancanderson/ingreedy"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.require_path = "lib"
|
19
|
+
|
20
|
+
s.add_dependency "parslet", "~> 1.7.0", ">= 1.7.0"
|
21
|
+
|
22
|
+
s.add_development_dependency "rake", "~> 0.9", ">= 0.9"
|
23
|
+
s.add_development_dependency "rspec", "~> 3.3.0", ">= 3.3.0"
|
24
|
+
s.add_development_dependency "coveralls", "~> 0.7.0", ">= 0.7.0"
|
25
|
+
s.add_development_dependency "pry"
|
26
|
+
end
|
data/lib/ingreedy.rb
CHANGED
@@ -1,20 +1,24 @@
|
|
1
|
-
path = File.expand_path(File.join(File.dirname(__FILE__),
|
1
|
+
path = File.expand_path(File.join(File.dirname(__FILE__), "ingreedy"))
|
2
2
|
|
3
|
-
require File.join(path,
|
4
|
-
require File.join(path,
|
5
|
-
require File.join(path,
|
3
|
+
require File.join(path, "case_insensitive_parser")
|
4
|
+
require File.join(path, "ingreedy_parser")
|
5
|
+
require File.join(path, "dictionary_collection")
|
6
6
|
|
7
7
|
module Ingreedy
|
8
|
-
|
9
|
-
|
8
|
+
def self.locale
|
9
|
+
@locale ||= nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.locale=(locale)
|
13
|
+
@locale = locale
|
14
|
+
end
|
10
15
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
def self.parse(query)
|
17
|
+
parser = Parser.new(query)
|
18
|
+
parser.parse
|
19
|
+
end
|
15
20
|
|
16
|
-
|
17
|
-
|
18
|
-
end
|
21
|
+
def self.dictionaries
|
22
|
+
@dictionaries ||= DictionaryCollection.new
|
19
23
|
end
|
20
24
|
end
|
@@ -1,7 +1,6 @@
|
|
1
|
-
require
|
1
|
+
require "parslet"
|
2
2
|
|
3
3
|
module Ingreedy
|
4
|
-
|
5
4
|
class AmountParser < Parslet::Parser
|
6
5
|
include CaseInsensitiveParser
|
7
6
|
|
@@ -10,16 +9,16 @@ module Ingreedy
|
|
10
9
|
end
|
11
10
|
|
12
11
|
rule(:integer) do
|
13
|
-
match(
|
12
|
+
match("[0-9]").repeat(1)
|
14
13
|
end
|
15
14
|
|
16
15
|
rule(:float) do
|
17
16
|
integer.maybe >>
|
18
|
-
|
17
|
+
float_delimiter >> integer
|
19
18
|
end
|
20
19
|
|
21
20
|
rule(:float_delimiter) do
|
22
|
-
str(
|
21
|
+
str(",") | str(".")
|
23
22
|
end
|
24
23
|
|
25
24
|
rule(:fraction) do
|
@@ -28,16 +27,16 @@ module Ingreedy
|
|
28
27
|
|
29
28
|
rule(:compound_simple_fraction) do
|
30
29
|
(integer.as(:integer_amount) >> whitespace).maybe >>
|
31
|
-
|
30
|
+
simple_fraction.as(:fraction_amount)
|
32
31
|
end
|
33
32
|
|
34
33
|
rule(:simple_fraction) do
|
35
|
-
integer >> match(
|
34
|
+
integer >> match("/") >> integer
|
36
35
|
end
|
37
36
|
|
38
37
|
rule(:compound_vulgar_fraction) do
|
39
38
|
(integer.as(:integer_amount) >> whitespace.maybe).maybe >>
|
40
|
-
|
39
|
+
vulgar_fraction.as(:fraction_amount)
|
41
40
|
end
|
42
41
|
|
43
42
|
rule(:vulgar_fraction) do
|
@@ -49,26 +48,26 @@ module Ingreedy
|
|
49
48
|
end
|
50
49
|
|
51
50
|
rule(:amount_unit_separator) do
|
52
|
-
whitespace | str(
|
51
|
+
whitespace | str("-")
|
53
52
|
end
|
54
53
|
|
55
54
|
rule(:amount) do
|
56
55
|
fraction |
|
57
|
-
|
58
|
-
|
59
|
-
|
56
|
+
float.as(:float_amount) |
|
57
|
+
integer.as(:integer_amount) |
|
58
|
+
word_digit.as(:word_integer_amount) >> amount_unit_separator
|
60
59
|
end
|
61
60
|
|
62
61
|
root(:amount)
|
63
62
|
|
64
63
|
private
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
def word_digits
|
66
|
+
Ingreedy.dictionaries.current.numbers.keys
|
67
|
+
end
|
69
68
|
|
70
|
-
|
71
|
-
|
72
|
-
|
69
|
+
def vulgar_fractions
|
70
|
+
Ingreedy.dictionaries.current.vulgar_fractions.keys
|
71
|
+
end
|
73
72
|
end
|
74
73
|
end
|
@@ -1,12 +1,10 @@
|
|
1
1
|
module Ingreedy
|
2
2
|
module CaseInsensitiveParser
|
3
|
-
|
4
3
|
def stri(str)
|
5
4
|
key_chars = str.split(//)
|
6
5
|
key_chars.
|
7
|
-
|
6
|
+
map! { |char| match["#{char.upcase}#{char.downcase}"] }.
|
8
7
|
reduce(:>>)
|
9
8
|
end
|
10
|
-
|
11
9
|
end
|
12
10
|
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
:units:
|
2
|
+
:cup:
|
3
|
+
- "cups"
|
4
|
+
- "cup"
|
5
|
+
- "c."
|
6
|
+
- "c"
|
7
|
+
:fluid_ounce:
|
8
|
+
- "fl. oz."
|
9
|
+
- "fl oz"
|
10
|
+
- "fluid ounce"
|
11
|
+
- "fluid ounces"
|
12
|
+
:gallon:
|
13
|
+
- "gal"
|
14
|
+
- "gal."
|
15
|
+
- "gallon"
|
16
|
+
- "gallons"
|
17
|
+
:ounce:
|
18
|
+
- "oz"
|
19
|
+
- "oz."
|
20
|
+
- "ounce"
|
21
|
+
- "ounces"
|
22
|
+
:pint:
|
23
|
+
- "pt"
|
24
|
+
- "pt."
|
25
|
+
- "pint"
|
26
|
+
- "pints"
|
27
|
+
:pound:
|
28
|
+
- "lb"
|
29
|
+
- "lb."
|
30
|
+
- "pound"
|
31
|
+
- "pounds"
|
32
|
+
:quart:
|
33
|
+
- "qt"
|
34
|
+
- "qt."
|
35
|
+
- "qts"
|
36
|
+
- "qts."
|
37
|
+
- "quart"
|
38
|
+
- "quarts"
|
39
|
+
:tablespoon:
|
40
|
+
- "tbsp."
|
41
|
+
- "tbsp"
|
42
|
+
- "T"
|
43
|
+
- "T."
|
44
|
+
- "tablespoon"
|
45
|
+
- "tablespoons"
|
46
|
+
- "tbs."
|
47
|
+
- "tbs"
|
48
|
+
:teaspoon:
|
49
|
+
- "tsp."
|
50
|
+
- "tsp"
|
51
|
+
- "t"
|
52
|
+
- "t."
|
53
|
+
- "teaspoon"
|
54
|
+
- "teaspoons"
|
55
|
+
:gram:
|
56
|
+
- "g"
|
57
|
+
- "g."
|
58
|
+
- "gr"
|
59
|
+
- "gr."
|
60
|
+
- "gram"
|
61
|
+
- "grams"
|
62
|
+
:kilogram:
|
63
|
+
- "kg"
|
64
|
+
- "kg."
|
65
|
+
- "kilogram"
|
66
|
+
- "kilograms"
|
67
|
+
:liter:
|
68
|
+
- "l"
|
69
|
+
- "l."
|
70
|
+
- "liter"
|
71
|
+
- "liters"
|
72
|
+
:milligram:
|
73
|
+
- "mg"
|
74
|
+
- "mg."
|
75
|
+
- "milligram"
|
76
|
+
- "milligrams"
|
77
|
+
:milliliter:
|
78
|
+
- "ml"
|
79
|
+
- "ml."
|
80
|
+
- "milliliter"
|
81
|
+
- "milliliters"
|
82
|
+
:pinch:
|
83
|
+
- "pinch"
|
84
|
+
- "pinches"
|
85
|
+
:dash:
|
86
|
+
- "dash"
|
87
|
+
- "dashes"
|
88
|
+
:touch:
|
89
|
+
- "touch"
|
90
|
+
- "touches"
|
91
|
+
:handful:
|
92
|
+
- "handful"
|
93
|
+
- "handfuls"
|
94
|
+
:stick:
|
95
|
+
- "stick"
|
96
|
+
- "sticks"
|
97
|
+
:clove:
|
98
|
+
- "cloves"
|
99
|
+
- "clove"
|
100
|
+
:can:
|
101
|
+
- "cans"
|
102
|
+
- "can"
|
103
|
+
:to_taste:
|
104
|
+
- "to taste"
|
105
|
+
:numbers:
|
106
|
+
a: 1
|
107
|
+
an: 1
|
108
|
+
zero: 0
|
109
|
+
one: 1
|
110
|
+
two: 2
|
111
|
+
three: 3
|
112
|
+
four: 4
|
113
|
+
five: 5
|
114
|
+
six: 6
|
115
|
+
seven: 7
|
116
|
+
eight: 8
|
117
|
+
nine: 9
|
118
|
+
ten: 10
|
119
|
+
eleven: 11
|
120
|
+
twelve: 12
|
121
|
+
thirteen: 13
|
122
|
+
fourteen: 14
|
123
|
+
fifteen: 15
|
124
|
+
sixteen: 16
|
125
|
+
seventeen: 17
|
126
|
+
eighteen: 18
|
127
|
+
nineteen: 19
|
128
|
+
twenty: 20
|
129
|
+
thirty: 30
|
130
|
+
forty: 40
|
131
|
+
fifty: 50
|
132
|
+
sixty: 60
|
133
|
+
seventy: 70
|
134
|
+
eighty: 80
|
135
|
+
ninety: 9
|
136
|
+
:prepositions:
|
137
|
+
- "of"
|
data/lib/ingreedy/dictionary.rb
CHANGED
@@ -3,7 +3,7 @@ module Ingreedy
|
|
3
3
|
attr_reader :units, :numbers, :prepositions
|
4
4
|
|
5
5
|
def initialize(entries = {})
|
6
|
-
@units = entries[:units] || raise(
|
6
|
+
@units = entries[:units] || raise("No units found in dictionary")
|
7
7
|
@numbers = entries[:numbers] || {}
|
8
8
|
@prepositions = entries[:prepositions] || []
|
9
9
|
end
|
@@ -11,24 +11,24 @@ module Ingreedy
|
|
11
11
|
# https://en.wikipedia.org/wiki/Number_Forms
|
12
12
|
def vulgar_fractions
|
13
13
|
{
|
14
|
-
"\u00BC" =>
|
15
|
-
"\u00BD" =>
|
16
|
-
"\u00BE" =>
|
17
|
-
"\u2150" =>
|
18
|
-
"\u2151" =>
|
19
|
-
"\u2152" =>
|
20
|
-
"\u2153" =>
|
21
|
-
"\u2154" =>
|
22
|
-
"\u2155" =>
|
23
|
-
"\u2156" =>
|
24
|
-
"\u2157" =>
|
25
|
-
"\u2158" =>
|
26
|
-
"\u2159" =>
|
27
|
-
"\u215A" =>
|
28
|
-
"\u215B" =>
|
29
|
-
"\u215C" =>
|
30
|
-
"\u215D" =>
|
31
|
-
"\u215E" =>
|
14
|
+
"\u00BC" => "1/4",
|
15
|
+
"\u00BD" => "1/2",
|
16
|
+
"\u00BE" => "3/4",
|
17
|
+
"\u2150" => "1/7",
|
18
|
+
"\u2151" => "1/9",
|
19
|
+
"\u2152" => "1/10",
|
20
|
+
"\u2153" => "1/3",
|
21
|
+
"\u2154" => "2/3",
|
22
|
+
"\u2155" => "1/5",
|
23
|
+
"\u2156" => "2/5",
|
24
|
+
"\u2157" => "3/5",
|
25
|
+
"\u2158" => "4/5",
|
26
|
+
"\u2159" => "1/6",
|
27
|
+
"\u215A" => "5/6",
|
28
|
+
"\u215B" => "1/8",
|
29
|
+
"\u215C" => "3/8",
|
30
|
+
"\u215D" => "5/8",
|
31
|
+
"\u215E" => "7/8",
|
32
32
|
}
|
33
33
|
end
|
34
34
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require_relative
|
1
|
+
require "yaml"
|
2
|
+
require_relative "dictionary"
|
3
3
|
|
4
4
|
module Ingreedy
|
5
5
|
class DictionaryCollection
|
@@ -26,7 +26,9 @@ module Ingreedy
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def load_yaml(locale)
|
29
|
-
path = File.expand_path(
|
29
|
+
path = File.expand_path(
|
30
|
+
File.join(File.dirname(__FILE__), "dictionaries", "#{locale}.yml"),
|
31
|
+
)
|
30
32
|
YAML.load_file(path)
|
31
33
|
rescue Errno::ENOENT
|
32
34
|
raise "No dictionary found for :#{locale} locale"
|