inci_score 4.1.4 → 4.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/catalog.yml +1 -0
- data/lib/inci_score/cli.rb +3 -4
- data/lib/inci_score/computer.rb +3 -5
- data/lib/inci_score/recognizer.rb +5 -6
- data/lib/inci_score/recognizer_rules.rb +12 -10
- data/lib/inci_score/response.rb +2 -5
- data/lib/inci_score/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07a79ba247cd43e62ef0988548df410598bf87cb653fd1540f8c6e419a31d03a
|
4
|
+
data.tar.gz: d0ea4f382824592d33d5bc6ec8bc5d3f88c6a704d24f093259a90da77bf3c6c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b92528b5d0ef275afce0db6c5d8039eb48daa2e08c098cd5d768dc61be10c2bfa72c24795dea1f28ebc54ef3287beb60449a4a41930e19283072a7c3948cc6f
|
7
|
+
data.tar.gz: e414756b761775502d54ab4e62e20385148a1a706fed3057ad5a74b47be7ee8274cddcde235d357851158e3e5b28f9961e7873dd675125291b1c226b6f2dc71f
|
data/config/catalog.yml
CHANGED
data/lib/inci_score/cli.rb
CHANGED
@@ -4,20 +4,19 @@ require 'optparse'
|
|
4
4
|
|
5
5
|
module InciScore
|
6
6
|
class CLI
|
7
|
-
attr_reader :args, :io
|
7
|
+
attr_reader :args, :io
|
8
8
|
attr_accessor :src
|
9
9
|
|
10
|
-
def initialize(args:, io: STDOUT
|
10
|
+
def initialize(args:, io: STDOUT)
|
11
11
|
@args = args
|
12
12
|
@io = io
|
13
|
-
@catalog = catalog
|
14
13
|
@src = nil
|
15
14
|
end
|
16
15
|
|
17
16
|
def call
|
18
17
|
parser.parse!(args)
|
19
18
|
return io.puts(%q{Specify inci list as: --src='aqua, parfum, etc'}) unless src
|
20
|
-
computer = Computer.new(src: src
|
19
|
+
computer = Computer.new(src: src)
|
21
20
|
io.puts computer.call
|
22
21
|
end
|
23
22
|
|
data/lib/inci_score/computer.rb
CHANGED
@@ -5,12 +5,11 @@ module InciScore
|
|
5
5
|
TOLERANCE = 30.0
|
6
6
|
DECIMALS = 2
|
7
7
|
|
8
|
-
attr_reader :src, :
|
8
|
+
attr_reader :src, :rules, :ingredients, :components, :unrecognized
|
9
9
|
|
10
|
-
def initialize(src:,
|
10
|
+
def initialize(src:, rules: Normalizer::DEFAULT_RULES)
|
11
11
|
@unrecognized = []
|
12
12
|
@src = src
|
13
|
-
@catalog = catalog
|
14
13
|
@rules = rules
|
15
14
|
@ingredients = Normalizer.new(src: src, rules: rules).call
|
16
15
|
@components = fetch_components
|
@@ -21,7 +20,6 @@ module InciScore
|
|
21
20
|
Response.new(components: components,
|
22
21
|
unrecognized: unrecognized,
|
23
22
|
score: score,
|
24
|
-
valid: valid?,
|
25
23
|
precision: precision)
|
26
24
|
end
|
27
25
|
|
@@ -41,7 +39,7 @@ module InciScore
|
|
41
39
|
|
42
40
|
def fetch_components
|
43
41
|
ingredients.map do |ingredient|
|
44
|
-
Recognizer.new(ingredient
|
42
|
+
Recognizer.new(ingredient).call.tap do |component|
|
45
43
|
unrecognized << ingredient unless component
|
46
44
|
end
|
47
45
|
end.compact
|
@@ -2,15 +2,14 @@
|
|
2
2
|
|
3
3
|
module InciScore
|
4
4
|
class Recognizer
|
5
|
-
DEFAULT_RULES = [Rules::Key, Rules::Levenshtein, Rules::
|
5
|
+
DEFAULT_RULES = [Rules::Key, Rules::Levenshtein, Rules::Hazard, Rules::Prefix, Rules::Tokens].freeze
|
6
6
|
|
7
7
|
Component = Struct.new(:name, :hazard)
|
8
8
|
|
9
|
-
attr_reader :ingredient, :
|
9
|
+
attr_reader :ingredient, :rules, :applied
|
10
10
|
|
11
|
-
def initialize(ingredient,
|
11
|
+
def initialize(ingredient, rules = DEFAULT_RULES, wrapper = Ingredient)
|
12
12
|
@ingredient = wrapper.new(ingredient)
|
13
|
-
@catalog = catalog
|
14
13
|
@rules = rules
|
15
14
|
@applied = []
|
16
15
|
freeze
|
@@ -20,7 +19,7 @@ module InciScore
|
|
20
19
|
return if ingredient.to_s.empty?
|
21
20
|
component = find_component
|
22
21
|
return unless component
|
23
|
-
Component.new(component,
|
22
|
+
Component.new(component, Config::CATALOG[component])
|
24
23
|
end
|
25
24
|
|
26
25
|
private
|
@@ -35,7 +34,7 @@ module InciScore
|
|
35
34
|
|
36
35
|
def apply(rule)
|
37
36
|
ingredient.values.map do |value|
|
38
|
-
rule.call(value
|
37
|
+
rule.call(value)
|
39
38
|
end.find(&:itself)
|
40
39
|
end
|
41
40
|
end
|
@@ -7,9 +7,9 @@ module InciScore
|
|
7
7
|
module Rules
|
8
8
|
TOLERANCE = 3
|
9
9
|
|
10
|
-
Key = ->(src
|
10
|
+
Key = ->(src) { src if Config::CATALOG.has_key?(src) }
|
11
11
|
|
12
|
-
Hazard = ->(src
|
12
|
+
Hazard = ->(src) { 'generic-hazard' if Config::HAZARDS.any? { |h| src.include?(h) } }
|
13
13
|
|
14
14
|
module Levenshtein
|
15
15
|
extend self
|
@@ -20,12 +20,12 @@ module InciScore
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def call(src
|
23
|
+
def call(src)
|
24
24
|
return if src.empty?
|
25
25
|
size = src.size
|
26
26
|
farthest = Result.new(nil, size)
|
27
27
|
initial = src[0]
|
28
|
-
result =
|
28
|
+
result = Config::CATALOG.reduce(farthest) do |nearest, (component, _)|
|
29
29
|
next nearest unless component.start_with?(initial)
|
30
30
|
next nearest if component.size > (size + TOLERANCE)
|
31
31
|
d = src.distance(component)
|
@@ -36,15 +36,15 @@ module InciScore
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
module
|
39
|
+
module Prefix
|
40
40
|
extend self
|
41
41
|
|
42
42
|
MIN_MEANINGFUL = 7
|
43
43
|
|
44
|
-
def call(src
|
44
|
+
def call(src)
|
45
45
|
return if src.size < TOLERANCE
|
46
46
|
digits = src[0, MIN_MEANINGFUL]
|
47
|
-
|
47
|
+
Config::CATALOG.detect { |component, _| component.start_with?(digits) }.to_a.first
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -53,9 +53,10 @@ module InciScore
|
|
53
53
|
|
54
54
|
UNMATCHABLE = %w[extract oil sodium acid sulfate].freeze
|
55
55
|
|
56
|
-
def call(src
|
56
|
+
def call(src)
|
57
|
+
return if src.size <= TOLERANCE
|
57
58
|
tokens(src).each do |token|
|
58
|
-
|
59
|
+
Config::CATALOG.each do |component, _|
|
59
60
|
return component if component.include?(token)
|
60
61
|
end
|
61
62
|
end
|
@@ -65,7 +66,8 @@ module InciScore
|
|
65
66
|
private
|
66
67
|
|
67
68
|
def tokens(src)
|
68
|
-
|
69
|
+
words = src.split(' ').map { |w| w.split('-') }.flatten
|
70
|
+
(words - UNMATCHABLE).reject { |t| t.size < TOLERANCE }.sort! { |a, b| b.size <=> a.size }
|
69
71
|
end
|
70
72
|
end
|
71
73
|
end
|
data/lib/inci_score/response.rb
CHANGED
@@ -4,19 +4,18 @@ require 'oj'
|
|
4
4
|
|
5
5
|
module InciScore
|
6
6
|
class Response
|
7
|
-
attr_reader :components, :score, :unrecognized, :
|
7
|
+
attr_reader :components, :score, :unrecognized, :precision
|
8
8
|
|
9
9
|
def initialize(options = {})
|
10
10
|
@components = options.fetch(:components) { [] }
|
11
11
|
@score = options.fetch(:score) { 0.0 }
|
12
12
|
@unrecognized = options.fetch(:unrecognized) { [] }
|
13
|
-
@valid = options.fetch(:valid) { false }
|
14
13
|
@precision = options.fetch(:precision) { 0 }
|
15
14
|
freeze
|
16
15
|
end
|
17
16
|
|
18
17
|
def to_json
|
19
|
-
data = { components: components.map(&:to_h), unrecognized: unrecognized, score: score,
|
18
|
+
data = { components: components.map(&:to_h), unrecognized: unrecognized, score: score, precision: precision }.freeze
|
20
19
|
Oj.dump(data, mode: :compat)
|
21
20
|
end
|
22
21
|
|
@@ -24,8 +23,6 @@ module InciScore
|
|
24
23
|
%Q{
|
25
24
|
TOTAL SCORE:
|
26
25
|
\t#{score}
|
27
|
-
VALID STATE:
|
28
|
-
\t#{valid}
|
29
26
|
PRECISION:
|
30
27
|
\t#{precision}
|
31
28
|
COMPONENTS:
|
data/lib/inci_score/version.rb
CHANGED