akiva 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +80 -0
- data/Guardfile +11 -0
- data/LICENSE +9 -0
- data/README.md +131 -0
- data/Rakefile +5 -0
- data/akiva.gemspec +38 -0
- data/lib/akiva.rb +11 -0
- data/lib/akiva/brain.rb +47 -0
- data/lib/akiva/cli.rb +58 -0
- data/lib/akiva/core_brain/actions/get_answers_from_subjects_and_properties.rb +73 -0
- data/lib/akiva/core_brain/actions/preparators/compare_two_subjects_properties_from_comparison_adjective.rb +19 -0
- data/lib/akiva/core_brain/actions/preparators/extract_property_from_comparison_adjective.rb +10 -0
- data/lib/akiva/core_brain/actions/preparators/regroup_separated_subjects.rb +10 -0
- data/lib/akiva/core_brain/actions/preparators/sort_answers_by_units.rb +35 -0
- data/lib/akiva/core_brain/actions/template.rb +23 -0
- data/lib/akiva/core_brain/filters/get_answers_from_subjects_and_properties.rb +15 -0
- data/lib/akiva/core_brain/filters/template.rb +9 -0
- data/lib/akiva/core_brain/formatters/boolean_from_comparison.rb +17 -0
- data/lib/akiva/core_brain/formatters/list_all_answers.rb +28 -0
- data/lib/akiva/core_brain/formatters/template.rb +19 -0
- data/lib/akiva/core_brain/helpers/comparison_adjectives_to_properties.rb +151 -0
- data/lib/akiva/core_brain/helpers/misc.rb +28 -0
- data/lib/akiva/core_brain/helpers/units.rb +167 -0
- data/lib/akiva/core_brain/loader.rb +3 -0
- data/lib/akiva/question.rb +88 -0
- data/lib/akiva/version.rb +9 -0
- data/spec/lib/akiva/brain_spec.rb +89 -0
- data/spec/lib/akiva/core_brain/actions/get_answers_from_subjects_and_properties_spec.rb +77 -0
- data/spec/lib/akiva/core_brain/actions/preparators/compare_two_subjects_properties_from_comparison_adjective_spec.rb +22 -0
- data/spec/lib/akiva/core_brain/actions/preparators/extract_property_from_comparison_adjective_spec.rb +11 -0
- data/spec/lib/akiva/core_brain/actions/preparators/regroup_separated_subjects_spec.rb +11 -0
- data/spec/lib/akiva/core_brain/actions/preparators/sort_answers_by_units_spec.rb +36 -0
- data/spec/lib/akiva/core_brain/filters/get_answers_from_subjects_and_properties_spec.rb +44 -0
- data/spec/lib/akiva/core_brain/formatters/boolean_from_comparison_spec.rb +38 -0
- data/spec/lib/akiva/core_brain/formatters/list_all_answers_spec.rb +60 -0
- data/spec/lib/akiva/core_brain/helpers/misc_spec.rb +26 -0
- data/spec/lib/akiva/core_brain/helpers/units_spec.rb +141 -0
- data/spec/lib/akiva/core_brain/question_spec.rb +170 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/akiva_brain_helpers.rb +13 -0
- data/spec/support/akiva_statements_helpers.rb +66 -0
- metadata +75 -16
@@ -0,0 +1,19 @@
|
|
1
|
+
Akiva::Brain.update do
|
2
|
+
|
3
|
+
add_action :compare_two_subjects_properties_from_comparison_adjective do |response|
|
4
|
+
answers_values = response[:answers].map{|hash| hash.last.first.last }
|
5
|
+
|
6
|
+
begin
|
7
|
+
units = Akiva::Brain::Helpers::Units.new(answers_values)
|
8
|
+
units.compare # in the future, you may want to pass a multiplicator here
|
9
|
+
rescue
|
10
|
+
# we fail silently for now, as the error probably comes from the data in TheBigDB anyway,
|
11
|
+
# which could well be invalid (= not convertible units)
|
12
|
+
else
|
13
|
+
property_comparator = Akiva::Brain::Helpers::ComparisonAdjectivesToProperties[response[:filter_captures]["comparison_adjective"]][:comparator]
|
14
|
+
response[:comparison_boolean_result] = (units.result == property_comparator)
|
15
|
+
end # begin
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Akiva::Brain.update do
|
2
|
+
|
3
|
+
add_action :extract_property_from_comparison_adjective do |response|
|
4
|
+
|
5
|
+
# Note: for now, we're only working with the first property listed for the adjective
|
6
|
+
response[:defined_properties] = [Akiva::Brain::Helpers::ComparisonAdjectivesToProperties[response[:filter_captures]["comparison_adjective"]][:properties].first]
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Akiva::Brain.update do
|
2
|
+
|
3
|
+
add_action :regroup_separated_subjects do |response|
|
4
|
+
response[:defined_subjects] ||= []
|
5
|
+
response[:filter_captures].each_pair do |name, capture|
|
6
|
+
response[:defined_subjects] << Akiva::Brain::Helpers.cleanup_articles(capture) if name =~ /\Asubject\d+/
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
Akiva::Brain.update do
|
2
|
+
|
3
|
+
add_action :sort_answers_by_units do |response|
|
4
|
+
# This is supposed to sort one answer for one property for multiple subjects,
|
5
|
+
# you may want to create another action to do something more.
|
6
|
+
|
7
|
+
answers_values = response[:answers].map{|hash| hash.last.first.last }
|
8
|
+
|
9
|
+
begin
|
10
|
+
units = Akiva::Brain::Helpers::Units.new(answers_values)
|
11
|
+
units.sort
|
12
|
+
rescue
|
13
|
+
# we fail silently for now, as the error probably comes from the data in TheBigDB anyway,
|
14
|
+
# which could well be invalid (= not convertible units)
|
15
|
+
else
|
16
|
+
response[:sorted_answers_by_units] = []
|
17
|
+
units.result.each do |instantiated_value|
|
18
|
+
response[:answers].each do |hash|
|
19
|
+
if instantiated_value == hash.last.first.last
|
20
|
+
response[:sorted_answers_by_units] <<
|
21
|
+
{
|
22
|
+
hash.first => {
|
23
|
+
hash.last.first.first => {
|
24
|
+
value: instantiated_value.to_s
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end # begin
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Akiva::Brain.update do
|
2
|
+
#
|
3
|
+
# add_action :name_of_action do |response|
|
4
|
+
# # 'response' is the same hash passed to all before_actions and to the action itself
|
5
|
+
# # The following keys are set from the start:
|
6
|
+
# response[:filter_matched] => {regex: /[original regex capturing the following (?<stuff_captured>.+)/, action: :name_of_action, [other options]}
|
7
|
+
# response[:filter_captures] => Hash of the captures made in the regex e.g. {"stuff_captured" => "words captured from the question"}
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# # If you're going to work with a huge action, you may want to pass a class instance instead of a block,
|
11
|
+
# # the method #process will be called with the current response as an argument.
|
12
|
+
# # The important difference is that you must return the new response when exiting #process, instead of modifying "response" as you do in blocks
|
13
|
+
#
|
14
|
+
# class MyCustomAction
|
15
|
+
# def process(response)
|
16
|
+
# # do stuff
|
17
|
+
# return new_response
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# add_action :name_of_action, MyCustomAction.new
|
22
|
+
#
|
23
|
+
# end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Akiva::Brain.update do
|
2
|
+
# The priority is based upon order of creation: last created -> highest priority.
|
3
|
+
|
4
|
+
add_filter :get_answers_from_subjects_and_properties,
|
5
|
+
/\A(?:What (?:is|are)|What's) the (?<properties>.+?) of (?:a |an |the )?(?<subjects>.+?)(?: \?)?\z/i,
|
6
|
+
formatter: :list_all_answers
|
7
|
+
|
8
|
+
add_filter :get_answers_from_subjects_and_properties,
|
9
|
+
/\AIs (?<subject1>.+?) (?<comparison_adjective>(#{Akiva::Brain::Helpers::ComparisonAdjectivesToProperties.keys.join("|")})?) than (?<subject2>.+?)(?: \?)?\z/i,
|
10
|
+
before_action: [:regroup_separated_subjects, :extract_property_from_comparison_adjective],
|
11
|
+
after_action: [:compare_two_subjects_properties_from_comparison_adjective],
|
12
|
+
formatter: :boolean_from_comparison
|
13
|
+
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Akiva::Brain.update do
|
2
|
+
#
|
3
|
+
# add_filter :name_of_the_action_that_will_be_called,
|
4
|
+
# /regex matching that will trigger this action/i,
|
5
|
+
# before_action: [:pre_action_one, :pre_action_two, [...]], # will be executed in this order
|
6
|
+
# after_action: [:post_action_one, :post_action_two, [...]], # will be executed in this order
|
7
|
+
# formatter: :name_of_formatter
|
8
|
+
#
|
9
|
+
# end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Akiva::Brain.update do
|
2
|
+
|
3
|
+
add_formatter :boolean_from_comparison do |response|
|
4
|
+
if response.has_key?(:comparison_boolean_result)
|
5
|
+
if response[:comparison_boolean_result]
|
6
|
+
response[:formatted] = "Yes"
|
7
|
+
else
|
8
|
+
response[:formatted] = "No"
|
9
|
+
end
|
10
|
+
|
11
|
+
if response.has_key?(:actions_chain) and response[:actions_chain].include?(:compare_two_subjects_properties_from_comparison_adjective)
|
12
|
+
response[:formatted] += " (#{Akiva::Brain.formatters[:list_all_answers].call(response)})"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
Akiva::Brain.update do
|
2
|
+
|
3
|
+
add_formatter :list_all_answers do |response|
|
4
|
+
|
5
|
+
describe_properties = lambda do |subject|
|
6
|
+
if (properties = response[:properties][subject]).size == 1 # if there is only one property
|
7
|
+
response[:answers][subject][properties.first]
|
8
|
+
else # if there are multiple properties
|
9
|
+
answers = response[:answers][subject].values
|
10
|
+
properties.map.with_index do |property, i|
|
11
|
+
property.capitalize + ": " + answers[i]
|
12
|
+
end.join(", ")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
if response[:subjects]
|
17
|
+
if response[:subjects].size == 1 # if there's only one subject
|
18
|
+
response[:formatted] = describe_properties.call(response[:subjects].first)
|
19
|
+
else # if there are multiple subjects
|
20
|
+
response[:formatted] = response[:subjects].map do |subject|
|
21
|
+
subject + " => " + describe_properties.call(subject)
|
22
|
+
end.join("; ")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Akiva::Brain.update do
|
2
|
+
#
|
3
|
+
# add_formatter :formatter_name do |response|
|
4
|
+
# # you must set response[:formatted] as a textual answer to the question
|
5
|
+
# end
|
6
|
+
#
|
7
|
+
# # If you're going to work with a huge formatter, you may want to pass a class instance instead of a block,
|
8
|
+
# # the method #process will be called with the current response as an argument.
|
9
|
+
# # The important difference is that you must return the new response when exiting #process, instead of modifying "response" as you do in blocks
|
10
|
+
#
|
11
|
+
# class MyCustomFormatter
|
12
|
+
# def process(response)
|
13
|
+
# # do stuff
|
14
|
+
# return new_response
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# add_formatter :name_of_action, MyCustomFormatter.new
|
19
|
+
# end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Akiva
|
2
|
+
module Brain
|
3
|
+
module Helpers
|
4
|
+
ComparisonAdjectivesToProperties = {
|
5
|
+
"bigger" => {properties: %w(size), comparator: "superior"},
|
6
|
+
"blacker" => {properties: %w(blackness), comparator: "superior"},
|
7
|
+
"bolder" => {properties: %w(boldness), comparator: "superior"},
|
8
|
+
"braver" => {properties: %w(courage), comparator: "superior"},
|
9
|
+
"brighter" => {properties: %w(brightness), comparator: "superior"},
|
10
|
+
"busier" => {properties: %w(business), comparator: "superior"},
|
11
|
+
# "cleaner" => {properties: %w(cleanliness)},
|
12
|
+
"clearer" => {properties: %w(clearness), comparator: "superior"},
|
13
|
+
# "cleverer" => {properties: %w(cleverness)},
|
14
|
+
"colder" => {properties: %w(temperature), comparator: "inferior"},
|
15
|
+
"cooler" => {properties: %w(temperature), comparator: "inferior"},
|
16
|
+
"darker" => {properties: %w(luminosity), comparator: "inferior"},
|
17
|
+
# "dearer" => {properties: %w(dearness)},
|
18
|
+
"deeper" => {properties: %w(depth), comparator: "superior"},
|
19
|
+
# "dirtier" => {properties: %w(dirtiness)},
|
20
|
+
"drier" => {properties: %w(humidity), comparator: "inferior"},
|
21
|
+
# "easier" => {properties: %w(easiness)},
|
22
|
+
# "fairer" => {properties: %w(fairness)},
|
23
|
+
"faster" => {properties: %w(speed), comparator: "superior"},
|
24
|
+
"fatter" => {properties: %w(fatness), comparator: "superior"},
|
25
|
+
# "finer" => {properties: %w()},
|
26
|
+
"funnier" => {properties: %w(funniness), comparator: "superior"},
|
27
|
+
"greater" => {properties: %w(greatness), comparator: "superior"},
|
28
|
+
"greener" => {properties: %w(greeness), comparator: "superior"},
|
29
|
+
# "happier" => {properties: %w(happiness)},
|
30
|
+
# "harder" => {properties: %w(hardness)},
|
31
|
+
"healthier" => {properties: %w(healthiness)},
|
32
|
+
"heavier" => {properties: ["weight", "average weight"], comparator: "superior"},
|
33
|
+
# "higher" => {properties: %w(highness)},
|
34
|
+
"hotter" => {properties: %w(temperature), comparator: "superior"},
|
35
|
+
# "kinder" => {properties: %w(kindness)},
|
36
|
+
"larger" => {properties: %w(size), comparator: "superior"},
|
37
|
+
# "later" => {properties: %w(timing)},
|
38
|
+
# "lazier" => {properties: %w(laziness)},
|
39
|
+
"lighter" => {properties: ["weight", "average weight"], comparator: "inferior"},
|
40
|
+
"longer" => {properties: %w(size duration), comparator: "superior"},
|
41
|
+
# "lower" => {properties: %w(lowerness)},
|
42
|
+
# "luckier" => {properties: %w(luck)},
|
43
|
+
# "madder" => {properties: %w(madness)},
|
44
|
+
# "merrier" => {properties: %w(merriness)},
|
45
|
+
# "narrower" => {properties: %w(narrowness)},
|
46
|
+
# "naughtier" => {properties: %w(naughtiness)},
|
47
|
+
# "nearer" => {properties: %w(nearness)},
|
48
|
+
"newer" => {properties: ["age", "creation date", "release date"], comparator: "inferior"},
|
49
|
+
# "noiser" => {properties: %w(noisiness)},
|
50
|
+
"older" => {properties: ["age", "creation date", "release date"], comparator: "superior"},
|
51
|
+
# "paler" => {properties: %w(paleness)},
|
52
|
+
"poorer" => {properties: ["net worth"], comparator: "inferior"},
|
53
|
+
# "prettier" => {properties: %w(prettiness)},
|
54
|
+
# "prouder" => {properties: %w(proudness)},
|
55
|
+
"quicker" => {properties: %w(speed), comparator: "superior"},
|
56
|
+
# "redder" => {properties: %w(redness)},
|
57
|
+
"richer" => {properties: ["net worth"], comparator: "superior"},
|
58
|
+
# "sadder" => {properties: %w(sadness)},
|
59
|
+
# "saffer" => {properties: %w(safeness)},
|
60
|
+
# "shallower" => {properties: %w(shallowness)},
|
61
|
+
# "sharper" => {properties: %w(sharpness)},
|
62
|
+
"shorter" => {properties: %w(size duration), comparator: "inferior"},
|
63
|
+
# "shallower" => {properties: %w(shallowness)},
|
64
|
+
"slower" => {properties: %w(speed), comparator: "inferior"},
|
65
|
+
"smaller" => {properties: %w(size duration), comparator: "inferior"},
|
66
|
+
# "smoother" => {properties: %w(smoothness)},
|
67
|
+
"stronger" => {properties: %w(power), comparator: "superior"},
|
68
|
+
# "sweeter" => {properties: %w(sweetness)},
|
69
|
+
"taller" => {properties: %w(height size), comparator: "superior"},
|
70
|
+
# "thicker" => {properties: %w(thickness)},
|
71
|
+
"thinner" => {properties: %w(size), comparator: "inferior"},
|
72
|
+
"tinier" => {properties: %w(size), comparator: "inferior"},
|
73
|
+
# "uglier" => {properties: %w(ugliness)},
|
74
|
+
"warmer" => {properties: %w(temperature), comparator: "superior"},
|
75
|
+
"weakness" => {properties: %w(power), comparator: "inferior"},
|
76
|
+
"wealthier" => {properties: ["net worth"], comparator: "superior"},
|
77
|
+
"wetter" => {properties: %w(humidity), comparator: "superior"},
|
78
|
+
# "whither" => {properties: %w()},
|
79
|
+
"wider" => {properties: %w(wideness size), comparator: "superior"},
|
80
|
+
# "wilder" => {properties: %w(wildness)},
|
81
|
+
# "wiser" => {properties: %w(wiseness)},
|
82
|
+
"younger" => {properties: %w(age), comparator: "inferior"},
|
83
|
+
|
84
|
+
|
85
|
+
"more ancient" => {properties: ["age", "creation date", "release date"], comparator: "inferior"},
|
86
|
+
# "more beautiful" => {properties: %w(looks)},
|
87
|
+
# "more brilliant" => {properties: %w(brilliantness)},
|
88
|
+
# "more careful" => {properties: %w(carefulness)},
|
89
|
+
# "more careless" => {properties: %w(carefulness)},
|
90
|
+
# "more cheerful" => {properties: %w(cheerfulness)},
|
91
|
+
# "more comfortable" => {properties: %w(comfortableness)},
|
92
|
+
# "more dangerous" => {properties: %w(dangerousness)},
|
93
|
+
# "more delightful" => {properties: %w(delightfulness)},
|
94
|
+
# "more difficult" => {properties: %w(difficultness)},
|
95
|
+
# "more enjoyable" => {properties: %w(enjoyability)},
|
96
|
+
# "more foolish" => {properties: %w(foolishness)},
|
97
|
+
# "more forgetful" => {properties: %w(forgetfulness)},
|
98
|
+
# "more frightening" => {properties: %w(frighteningness)},
|
99
|
+
# "more generous" => {properties: %w(generousness)},
|
100
|
+
# "more handsome" => {properties: %w(handsomeness)},
|
101
|
+
# "more helpful" => {properties: %w(helpfulness)},
|
102
|
+
# "more ignorant" => {properties: %w(ignorantness)},
|
103
|
+
# "more important" => {properties: %w(importantness)},
|
104
|
+
# "more intelligent" => {properties: %w(intelligentness)},
|
105
|
+
# "more interesting" => {properties: %w(interestingness)},
|
106
|
+
# "more pleasant" => {properties: %w(pleasantness)},
|
107
|
+
"more powerful" => {properties: %w(power), comparator: "superior"},
|
108
|
+
"more prosperous" => {properties: ["gross domestic product"], comparator: "superior"},
|
109
|
+
# "more sensible" => {properties: %w(sensibleness)},
|
110
|
+
# "more terrible" => {properties: %w(terribleness)},
|
111
|
+
# "more thoughtful" => {properties: %w(thoughtfulness)},
|
112
|
+
# "more unusual" => {properties: %w(unusualness)},
|
113
|
+
# "more useful" => {properties: %w(usefulness)},
|
114
|
+
"more valuable" => {properties: ["worth", "price"], comparator: "superior"},
|
115
|
+
# "more wonderful" => {properties: %w(wonderfulness)},
|
116
|
+
|
117
|
+
"less ancient" => {properties: ["age", "creation date", "release date"], comparator: "inferior"},
|
118
|
+
# "less beautiful" => {properties: %w(looks)},
|
119
|
+
# "less brilliant" => {properties: %w(brilliantness)},
|
120
|
+
# "less careful" => {properties: %w(carefulness)},
|
121
|
+
# "less careless" => {properties: %w(carefulness)},
|
122
|
+
# "less cheerful" => {properties: %w(cheerfulness)},
|
123
|
+
# "less comfortable" => {properties: %w(comfortableness)},
|
124
|
+
# "less dangerous" => {properties: %w(dangerousness)},
|
125
|
+
# "less delightful" => {properties: %w(delightfulness)},
|
126
|
+
# "less difficult" => {properties: %w(difficultness)},
|
127
|
+
# "less enjoyable" => {properties: %w(enjoyability)},
|
128
|
+
# "less foolish" => {properties: %w(foolishness)},
|
129
|
+
# "less forgetful" => {properties: %w(forgetfulness)},
|
130
|
+
# "less frightening" => {properties: %w(frighteningness)},
|
131
|
+
# "less generous" => {properties: %w(generousness)},
|
132
|
+
# "less handsome" => {properties: %w(handsomeness)},
|
133
|
+
# "less helpful" => {properties: %w(helpfulness)},
|
134
|
+
# "less ignorant" => {properties: %w(ignorantness)},
|
135
|
+
# "less important" => {properties: %w(importantness)},
|
136
|
+
# "less intelligent" => {properties: %w(intelligentness)},
|
137
|
+
# "less interesting" => {properties: %w(interestingness)},
|
138
|
+
# "less pleasant" => {properties: %w(pleasantness)},
|
139
|
+
"less powerful" => {properties: %w(power), comparator: "superior"},
|
140
|
+
"less prosperous" => {properties: ["gross domestic product"], comparator: "superior"},
|
141
|
+
# "less sensible" => {properties: %w(sensibleness)},
|
142
|
+
# "less terrible" => {properties: %w(terribleness)},
|
143
|
+
# "less thoughtful" => {properties: %w(thoughtfulness)},
|
144
|
+
# "less unusual" => {properties: %w(unusualness)},
|
145
|
+
# "less useful" => {properties: %w(usefulness)},
|
146
|
+
"less valuable" => {properties: ["worth", "price"], comparator: "superior"},
|
147
|
+
# "less wonderful" => {properties: %w(wonderfulness)},
|
148
|
+
}
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Akiva
|
2
|
+
module Brain
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
def self.degroup_multiple_values(string)
|
6
|
+
# NOTE: we probably should check the dictionary here to improve the values detections,
|
7
|
+
# and limit the number of requests made.
|
8
|
+
values = []
|
9
|
+
|
10
|
+
string.split(" && ").each do |sub0|
|
11
|
+
sub0.split(",").each do |sub1|
|
12
|
+
sub1.split(" and ").each do |sub2|
|
13
|
+
val = sub2.strip
|
14
|
+
val.gsub!(/\A(a|an|the) /i, "")
|
15
|
+
values << val
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
values.uniq
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.cleanup_articles(string)
|
24
|
+
string.gsub(/\A(a|an|the) /i, "")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module Akiva
|
2
|
+
module Brain
|
3
|
+
module Helpers
|
4
|
+
class Units
|
5
|
+
class UnitNotRecognizedError < StandardError; end
|
6
|
+
class IncompatibleUnitsError < StandardError; end
|
7
|
+
class TooLongToInstantiateError < StandardError; end
|
8
|
+
class NeedTwoValuesToCompareError < StandardError; end
|
9
|
+
class CantCompareMoreThanTwoValuesError < StandardError; end
|
10
|
+
class InputMustBeAnArray < StandardError; end
|
11
|
+
|
12
|
+
attr_reader :input, :result, :instantiated_values, :first_value, :last_value
|
13
|
+
|
14
|
+
def initialize(input)
|
15
|
+
@input = input
|
16
|
+
end
|
17
|
+
|
18
|
+
def compare(options = {})
|
19
|
+
check_presence_of_values
|
20
|
+
check_type_of_values
|
21
|
+
check_length_of_values_for_comparison
|
22
|
+
instantiate_values
|
23
|
+
check_compatibility_of_units
|
24
|
+
|
25
|
+
@first_value = @instantiated_values.first
|
26
|
+
@last_value = @instantiated_values.last
|
27
|
+
|
28
|
+
if multiplicators = options[:multiplicators]
|
29
|
+
@first_value = (@first_value * multiplicators[0]) if multiplicators[0]
|
30
|
+
@last_value = (@last_value * multiplicators[1]) if multiplicators[1]
|
31
|
+
end
|
32
|
+
|
33
|
+
@first_value, @last_value = self.class.adjust_units(@first_value, @last_value)
|
34
|
+
|
35
|
+
if @first_value > @last_value
|
36
|
+
@result = "superior"
|
37
|
+
elsif @first_value < @last_value
|
38
|
+
@result = "inferior"
|
39
|
+
else
|
40
|
+
@result = "equal"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def sort
|
45
|
+
check_presence_of_values
|
46
|
+
check_type_of_values
|
47
|
+
check_maximum_length_of_values
|
48
|
+
instantiate_values
|
49
|
+
check_compatibility_of_units
|
50
|
+
|
51
|
+
@result = @instantiated_values.sort
|
52
|
+
end
|
53
|
+
|
54
|
+
def convert(new_unit = "")
|
55
|
+
instantiate_values
|
56
|
+
|
57
|
+
begin
|
58
|
+
new_instantiated_value = @instantiated_value.convert_to(new_unit)
|
59
|
+
@result = new_instantiated_value.scalar.to_f.to_s
|
60
|
+
@result = @result[0..@result.size-3] if @result.match(/\.0$/) # 135.0 => 135
|
61
|
+
rescue ArgumentError => error_string
|
62
|
+
if error_string.to_s.include?("Unit not recognized") or error_string.to_s.include?("No Unit Specified")
|
63
|
+
raise UnitNotRecognizedError
|
64
|
+
elsif error_string.to_s.include?("Incompatible Units")
|
65
|
+
raise IncompatibleUnitsError
|
66
|
+
else
|
67
|
+
raise
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def self.adjust_units(value_1, value_2)
|
74
|
+
if ((value_1.to_s.include?("e+") or value_1.to_s.include?("e-")) and (!value_2.to_s.include?("e+") and !value_2.to_s.include?("e-"))) or value_1.scalar.to_s.size > value_2.scalar.to_s.size
|
75
|
+
value_1 = value_1.convert_to(value_2)
|
76
|
+
elsif ((!value_1.to_s.include?("e+") and !value_1.to_s.include?("e-")) and (value_2.to_s.include?("e+") or value_2.to_s.include?("e-"))) or value_1.scalar.to_s.size < value_2.scalar.to_s.size
|
77
|
+
value_2 = value_2.convert_to(value_1)
|
78
|
+
end
|
79
|
+
|
80
|
+
[value_1, value_2]
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
def check_presence_of_value
|
85
|
+
if @input.nil?
|
86
|
+
raise NeedTwoValuesToCompareError
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def check_presence_of_values
|
91
|
+
if @input.nil?
|
92
|
+
raise NeedTwoValuesToCompareError
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def check_type_of_values
|
97
|
+
unless @input.is_a?(Array) or @input.is_a?(Hash)
|
98
|
+
raise InputMustBeAnArray
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def check_length_of_values_for_comparison
|
103
|
+
if @input.size < 2
|
104
|
+
raise NeedTwoValuesToCompareError
|
105
|
+
elsif @input.size > 2
|
106
|
+
raise CantCompareMoreThanTwoValuesError
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def check_maximum_length_of_values
|
111
|
+
if @input.size > 100
|
112
|
+
raise CantCompareMoreThanTwoValuesError
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def instantiate_values
|
117
|
+
# we accept strings here specially for the "convert" action
|
118
|
+
# everywhere else it is filtered with check_type_of_values
|
119
|
+
|
120
|
+
if @input.is_a?(String)
|
121
|
+
values_strings = [@input]
|
122
|
+
elsif @input.is_a?(Hash)
|
123
|
+
values_strings = @input.values
|
124
|
+
else
|
125
|
+
values_strings = @input
|
126
|
+
end
|
127
|
+
|
128
|
+
begin
|
129
|
+
# we check how much time it takes because it can be REALLY slow be big textual values
|
130
|
+
# e.g. "900000000000000000000000000000000000 meters" took 5secs to instantiate (!)
|
131
|
+
Timeout::timeout(0.1) do
|
132
|
+
if @input.is_a?(String)
|
133
|
+
@instantiated_value = Unit(@input)
|
134
|
+
elsif values_strings.is_a?(Array)
|
135
|
+
@instantiated_values = values_strings.map{|_| Unit(_) }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
rescue Timeout::Error
|
139
|
+
raise CantCompareMoreThanTwoValuesError
|
140
|
+
return false
|
141
|
+
rescue ArgumentError => error_string
|
142
|
+
if error_string.to_s.include?("Unit not recognized")
|
143
|
+
raise UnitNotRecognizedError
|
144
|
+
return false
|
145
|
+
else
|
146
|
+
raise # error unknown
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def check_compatibility_of_units
|
152
|
+
# we simply check if it can be sorted
|
153
|
+
begin
|
154
|
+
@instantiated_values.sort
|
155
|
+
rescue ArgumentError => error_string
|
156
|
+
if error_string.to_s.include?("Incompatible Units")
|
157
|
+
raise IncompatibleUnitsError.new(error_string)
|
158
|
+
else
|
159
|
+
raise
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|