akiva 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +80 -0
  6. data/Guardfile +11 -0
  7. data/LICENSE +9 -0
  8. data/README.md +131 -0
  9. data/Rakefile +5 -0
  10. data/akiva.gemspec +38 -0
  11. data/lib/akiva.rb +11 -0
  12. data/lib/akiva/brain.rb +47 -0
  13. data/lib/akiva/cli.rb +58 -0
  14. data/lib/akiva/core_brain/actions/get_answers_from_subjects_and_properties.rb +73 -0
  15. data/lib/akiva/core_brain/actions/preparators/compare_two_subjects_properties_from_comparison_adjective.rb +19 -0
  16. data/lib/akiva/core_brain/actions/preparators/extract_property_from_comparison_adjective.rb +10 -0
  17. data/lib/akiva/core_brain/actions/preparators/regroup_separated_subjects.rb +10 -0
  18. data/lib/akiva/core_brain/actions/preparators/sort_answers_by_units.rb +35 -0
  19. data/lib/akiva/core_brain/actions/template.rb +23 -0
  20. data/lib/akiva/core_brain/filters/get_answers_from_subjects_and_properties.rb +15 -0
  21. data/lib/akiva/core_brain/filters/template.rb +9 -0
  22. data/lib/akiva/core_brain/formatters/boolean_from_comparison.rb +17 -0
  23. data/lib/akiva/core_brain/formatters/list_all_answers.rb +28 -0
  24. data/lib/akiva/core_brain/formatters/template.rb +19 -0
  25. data/lib/akiva/core_brain/helpers/comparison_adjectives_to_properties.rb +151 -0
  26. data/lib/akiva/core_brain/helpers/misc.rb +28 -0
  27. data/lib/akiva/core_brain/helpers/units.rb +167 -0
  28. data/lib/akiva/core_brain/loader.rb +3 -0
  29. data/lib/akiva/question.rb +88 -0
  30. data/lib/akiva/version.rb +9 -0
  31. data/spec/lib/akiva/brain_spec.rb +89 -0
  32. data/spec/lib/akiva/core_brain/actions/get_answers_from_subjects_and_properties_spec.rb +77 -0
  33. data/spec/lib/akiva/core_brain/actions/preparators/compare_two_subjects_properties_from_comparison_adjective_spec.rb +22 -0
  34. data/spec/lib/akiva/core_brain/actions/preparators/extract_property_from_comparison_adjective_spec.rb +11 -0
  35. data/spec/lib/akiva/core_brain/actions/preparators/regroup_separated_subjects_spec.rb +11 -0
  36. data/spec/lib/akiva/core_brain/actions/preparators/sort_answers_by_units_spec.rb +36 -0
  37. data/spec/lib/akiva/core_brain/filters/get_answers_from_subjects_and_properties_spec.rb +44 -0
  38. data/spec/lib/akiva/core_brain/formatters/boolean_from_comparison_spec.rb +38 -0
  39. data/spec/lib/akiva/core_brain/formatters/list_all_answers_spec.rb +60 -0
  40. data/spec/lib/akiva/core_brain/helpers/misc_spec.rb +26 -0
  41. data/spec/lib/akiva/core_brain/helpers/units_spec.rb +141 -0
  42. data/spec/lib/akiva/core_brain/question_spec.rb +170 -0
  43. data/spec/spec_helper.rb +37 -0
  44. data/spec/support/akiva_brain_helpers.rb +13 -0
  45. data/spec/support/akiva_statements_helpers.rb +66 -0
  46. metadata +75 -16
@@ -0,0 +1,26 @@
1
+ require "spec_helper"
2
+
3
+ describe "Akiva CoreBrain Helpers Misc" do
4
+ describe "degroup_multiple_values" do
5
+ it "correctly works with « width »" do
6
+ a = Akiva::Brain::Helpers.degroup_multiple_values("width")
7
+ a.should == ["width"]
8
+ end
9
+
10
+ it "correctly works with « width and height »" do
11
+ a = Akiva::Brain::Helpers.degroup_multiple_values("width and height")
12
+ a.should == ["width", "height"]
13
+ end
14
+
15
+ it "correctly works with « width, height and weight »" do
16
+ a = Akiva::Brain::Helpers.degroup_multiple_values("width, height and weight")
17
+ a.should == ["width", "height", "weight"]
18
+ end
19
+
20
+ it "correctly works with « the X, an Y and a Z »" do
21
+ a = Akiva::Brain::Helpers.degroup_multiple_values("the X, an Y and a Z")
22
+ a.should == ["X", "Y", "Z"]
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,141 @@
1
+ require "spec_helper"
2
+
3
+ describe "Akiva CoreBrain Helper: Units" do
4
+ describe "compare" do
5
+ context "with incomparable units" do
6
+ it "raises the correct exception" do
7
+ u = Akiva::Brain::Helpers::Units.new(["100 meters", "7 grams"])
8
+ expect { u.compare }.to raise_error(Akiva::Brain::Helpers::Units::IncompatibleUnitsError)
9
+ end
10
+ end
11
+
12
+ context "with unknown units" do
13
+ it "raises the correct exception" do
14
+ u = Akiva::Brain::Helpers::Units.new(["100 meters", "7 zigzagzoug"])
15
+ expect { u.compare }.to raise_error(Akiva::Brain::Helpers::Units::UnitNotRecognizedError)
16
+ end
17
+ end
18
+
19
+ context "with no values" do
20
+ it "raises the correct exception" do
21
+ u = Akiva::Brain::Helpers::Units.new(nil)
22
+ expect { u.compare }.to raise_error(Akiva::Brain::Helpers::Units::NeedTwoValuesToCompareError)
23
+
24
+ u = Akiva::Brain::Helpers::Units.new([])
25
+ expect { u.compare }.to raise_error(Akiva::Brain::Helpers::Units::NeedTwoValuesToCompareError)
26
+ end
27
+ end
28
+
29
+ context "with in an incorrect type of values" do
30
+ it "raises the correct exception" do
31
+ u = Akiva::Brain::Helpers::Units.new("my values !")
32
+ expect { u.compare }.to raise_error(Akiva::Brain::Helpers::Units::InputMustBeAnArray)
33
+ end
34
+ end
35
+
36
+ context "with not enough values" do
37
+ it "raises the correct exception" do
38
+ u = Akiva::Brain::Helpers::Units.new(["100 meters"])
39
+ expect { u.compare }.to raise_error(Akiva::Brain::Helpers::Units::NeedTwoValuesToCompareError)
40
+ end
41
+ end
42
+
43
+ context "with too many values" do
44
+ it "raises the correct exception" do
45
+ u = Akiva::Brain::Helpers::Units.new(["100 meters", "9 cm", "45 dm"])
46
+ expect { u.compare }.to raise_error(Akiva::Brain::Helpers::Units::CantCompareMoreThanTwoValuesError)
47
+ end
48
+ end
49
+
50
+ context "with different values" do
51
+ it "gives the correct answer" do
52
+ u = Akiva::Brain::Helpers::Units.new(["120 cm", "100 meters"])
53
+ u.compare
54
+ u.result.should == "inferior"
55
+ end
56
+ end
57
+
58
+ context "with the same values" do
59
+ it "gives the correct answer" do
60
+ u = Akiva::Brain::Helpers::Units.new(["120 cm", "1.2 m"])
61
+ u.compare
62
+ u.result.should == "equal"
63
+ end
64
+ end
65
+
66
+ context "with multiplicators" do
67
+ it "gives the correct answer" do
68
+ u = Akiva::Brain::Helpers::Units.new(["120 cm", "1.2 m"])
69
+ u.compare(multiplicators: [nil, 2])
70
+ u.result.should == "inferior"
71
+ end
72
+ end
73
+ end
74
+
75
+
76
+ it "can adjust units" do
77
+ result = Akiva::Brain::Helpers::Units.adjust_units(Unit("25000000 g"), Unit("5000 kg"))
78
+ result.should == [Unit("25000 kg"), Unit("5000 kg")]
79
+ end
80
+
81
+
82
+ describe "sort" do
83
+ context "with incomparable units" do
84
+ it "raises the correct exception" do
85
+ u = Akiva::Brain::Helpers::Units.new(["100 meters", "7 grams"])
86
+ expect { u.sort }.to raise_error(Akiva::Brain::Helpers::Units::IncompatibleUnitsError)
87
+ end
88
+ end
89
+
90
+ context "with unknown units" do
91
+ it "raises the correct exception" do
92
+ u = Akiva::Brain::Helpers::Units.new(["100 meters", "7 zigzagzoug"])
93
+ expect { u.sort }.to raise_error(Akiva::Brain::Helpers::Units::UnitNotRecognizedError)
94
+ end
95
+ end
96
+
97
+ context "with no values" do
98
+ it "raises the correct exception" do
99
+ u = Akiva::Brain::Helpers::Units.new(nil)
100
+ expect { u.sort }.to raise_error(Akiva::Brain::Helpers::Units::NeedTwoValuesToCompareError)
101
+ end
102
+ end
103
+
104
+ context "with too many values" do
105
+ it "raises the correct exception" do
106
+ u = Akiva::Brain::Helpers::Units.new(101.times.map{ Unit("#{rand(150)} cm") })
107
+ expect { u.sort }.to raise_error(Akiva::Brain::Helpers::Units::CantCompareMoreThanTwoValuesError)
108
+ end
109
+ end
110
+
111
+ context "with different values" do
112
+ it "gives the correct answer" do
113
+ u = Akiva::Brain::Helpers::Units.new(["120 cm", "100 meters", "7 m", "12.3 m"])
114
+ u.sort
115
+ u.result.should == ["120 cm", "7 m", "12.3 m", "100 m"]
116
+ end
117
+ end
118
+ end
119
+
120
+
121
+
122
+
123
+ describe "convert" do
124
+ context "with unknown units" do
125
+ it "raises the correct exception" do
126
+ u = Akiva::Brain::Helpers::Units.new("100 meters")
127
+ expect { u.convert("cmZZZ") }.to raise_error(Akiva::Brain::Helpers::Units::UnitNotRecognizedError)
128
+ end
129
+ end
130
+
131
+ context "with acceptable params" do
132
+ it "gives the correct answer" do
133
+ u = Akiva::Brain::Helpers::Units.new("100 ly")
134
+ u.convert("cm")
135
+ u.result.should == "9.460528412464108e+19"
136
+ end
137
+ end
138
+
139
+ end
140
+
141
+ end
@@ -0,0 +1,170 @@
1
+ require "spec_helper"
2
+
3
+ describe "Akiva CoreBrain" do
4
+
5
+ describe "answers" do
6
+ it "« What's the weight of an iPhone 5s »" do
7
+ stub_statements(:a0)
8
+
9
+ question = Akiva::Question.new("What's the weight of an iPhone 5s").process
10
+ question.response[:formatted].should == "112 grams"
11
+ end
12
+
13
+ it "executes on « What are the weight and height of an iPhone 5s »" do
14
+ stub_statements(:a0, :a1, :a2)
15
+
16
+ question = Akiva::Question.new("What are the weight and height of an iPhone 5s").process
17
+ question.response[:formatted].should == "Weight: 112 grams, Height: 123.8 mm"
18
+ end
19
+
20
+ it "executes on « What are the weight of an iPhone 5s and a Galaxy S4 »" do
21
+ stub_statements(:a0, :a3, :a4)
22
+
23
+ question = Akiva::Question.new("What are the weight of an iPhone 5s and a Galaxy S4").process
24
+ question.response[:formatted].should == "iPhone 5s => 112 grams; Galaxy S4 => 130 grams"
25
+ end
26
+
27
+ it "executes on « What are the weight and height of an iPhone 5s and a Galaxy S4 »" do
28
+ stub_statements(:a0, :a1, :a2, :a3, :a4, :a5, :a6)
29
+
30
+ question = Akiva::Question.new("What are the weight and height of an iPhone 5s and a Galaxy S4").process
31
+ question.response[:formatted].should == "iPhone 5s => Weight: 112 grams, Height: 123.8 mm; Galaxy S4 => Weight: 130 grams, Height: 136.6 mm"
32
+ end
33
+
34
+ it "executes on « What is the salary of the Director of Public Affairs and Communication »" do
35
+ stub_statements(:b0)
36
+
37
+ question = Akiva::Question.new("What is the salary of the Director of Public Affairs and Communication").process
38
+ question.response[:formatted].should == "$10,000"
39
+ end
40
+
41
+ it "executes on « What is the salary and benefits of the Director of Public Affairs and Communication »" do
42
+ stub_statements(:b0, :b1, :b2)
43
+
44
+ question = Akiva::Question.new("What is the salary and benefits of the Director of Public Affairs and Communication").process
45
+ question.response[:formatted].should == "Salary: $10,000, Benefits: Dental"
46
+ end
47
+
48
+ it "executes on « Is the iPhone 5s heavier than the Galaxy S4 »" do
49
+ stub_statements(:a0, :a3, :a4)
50
+
51
+ question = Akiva::Question.new("Is the iPhone 5s heavier than the Galaxy S4").process
52
+ question.response[:formatted].should == "No (iPhone 5s => 112 grams; Galaxy S4 => 130 grams)"
53
+ end
54
+
55
+ end
56
+
57
+ describe "doesn't yet answer", hidden: false do
58
+
59
+ # Please remember that for each question listed here,
60
+ # you should create tests in an existing file (or create a new one for a new action) in filters/, same for actions/ and formatters/ if necessary
61
+ # Also note:
62
+ # - they are not in order of importance, just pick one you like!
63
+ # - if the data for answering the question is not in TheBigDB (very likely), just add it! Or if you don't have enough data to add it yourself, ask for help on the forum, someone will surely land a hand to make it happen!
64
+ # - the list is obviously non exhaustive, please add questions you wish Akiva to answer
65
+
66
+ it "answers « How long does a tiger live »"
67
+ it "answers « How long is the george washington bridge »"
68
+ it "answers « How long is the movie ted »"
69
+ it "answers « How long is the year on Pluto »"
70
+ it "answers « How many divorces did Larry King have »"
71
+ it "answers « How many ethnic groups exist in Nigeria »"
72
+ it "answers « How many feet are there in a kilometer »"
73
+ it "answers « How many languages are spoken in Afghanistan »"
74
+ it "answers « How many people live in Israel »"
75
+ it "answers « How many people live on Earth »"
76
+ it "answers « How much does it cost to study at MIT »"
77
+ it "answers « How old is Elon Musk »"
78
+
79
+ it "answers « In what year did Isaac Bashevis Singer receive a Nobel Prize »"
80
+ it "answers « In what year did Mozart die »"
81
+
82
+ it "answers « Of all European countries, which has the smallest area »"
83
+ it "answers « Show the capital of the 2nd largest country in Asia »"
84
+ it "answers « What countries speak Portuguese »"
85
+ it "answers « What country in the world has the longest coastline »"
86
+ it "answers « What invention is Marconi responsible for »"
87
+ it "answers « What is Jupiter's atmosphere made of »"
88
+ it "answers « What is the atmosphere of Jupiter like »"
89
+ it "answers « What is the capital of Greenland »"
90
+ it "answers « What is the height requirement for space mountain »"
91
+ it "answers « What is the longest river in the world »"
92
+ it "answers « What is the terrain like in Mexico »"
93
+ it "answers « What music did Debussy compose »"
94
+ it "answers « What planet has the smallest surface area »"
95
+ it "answers « What South-American country has the largest population »"
96
+ it "answers « What's the definition of dynamic programming »"
97
+ it "answers « What's the largest city in Florida »"
98
+
99
+ it "answers « When did Abraham Lincoln die »"
100
+ it "answers « When was Beethoven born »"
101
+ it "answers « When was George Washington born »"
102
+ it "answers « When was the constitution adopted in the most populous country in Africa »"
103
+
104
+ it "answers « Where did Natalie Portman go to college »"
105
+ it "answers « Where do people speak Urdu »"
106
+ it "answers « Where was Yehudi Menuhin born »"
107
+
108
+ it "answers « Which is deeper, the Baltic Sea or the North Sea »"
109
+
110
+ it "answers « Who composed the opera Semiramide »"
111
+ it "answers « Who created the first typewriter »"
112
+ it "answers « Who directed The Birds »"
113
+ it "answers « Who first discovered radiocarbon dating »"
114
+ it "answers « Who founded Napster »"
115
+ it "answers « Who invented the first telephone »"
116
+ it "answers « Who invented the telegraph »"
117
+ it "answers « Who is the producer of the Titanic »"
118
+ it "answers « Who produced Ferris bueller's day off »"
119
+ it "answers « Who was governor of California during Bill Clinton presidency »"
120
+ it "answers « Who was president in 1881 »"
121
+ it "answers « Who was the fifth president of the United States »"
122
+ it "answers « Who was vice president under Thomas Jefferson »"
123
+ it "answers « Who won the Nobel prize for literature in 1987 »"
124
+ it "answers « Who wrote the Gift of the Magi »"
125
+ it "answers « Who wrote the Grand Design »"
126
+ it "answers « Who wrote the music to Star Wars »"
127
+ end
128
+
129
+ describe "with a mix a instances and blocks as actions and formatters" do
130
+ it "correctly executes each one of them" do
131
+ class MyCustomAction
132
+ def process(response)
133
+ new_response = response.merge(ran_through_custom_action: true)
134
+ new_response
135
+ end
136
+ end
137
+
138
+ class MyCustomFormatter
139
+ def process(response)
140
+ new_response = response.merge(ran_through_custom_formatter: true)
141
+ new_response
142
+ end
143
+ end
144
+
145
+ Akiva::Brain.update do
146
+ add_filter :foobar, /\Aexecute foobar\Z/, before_action: [:pre_action_one, :pre_action_two], formatter: :custom_formatter
147
+
148
+ add_action :foobar do |response|
149
+ response[:ran_through_main_action] = true
150
+ end
151
+
152
+ add_action :pre_action_one do |response|
153
+ response[:ran_through_action_one] = true
154
+ end
155
+
156
+ add_action :pre_action_two, MyCustomAction.new
157
+
158
+ add_formatter :custom_formatter, MyCustomFormatter.new
159
+ end
160
+
161
+ question = Akiva::Question.new("execute foobar").run_filters.execute_action.execute_formatter
162
+ question.response.should include(ran_through_main_action: true)
163
+ question.response.should include(ran_through_action_one: true)
164
+ question.response.should include(ran_through_custom_action: true)
165
+ question.response.should include(ran_through_custom_formatter: true)
166
+
167
+ end
168
+ end
169
+
170
+ end
@@ -0,0 +1,37 @@
1
+ require "rspec"
2
+ require "webmock/rspec"
3
+ require "awesome_print"
4
+ require "akiva"
5
+
6
+ # Requires supporting ruby files with custom matchers and macros, etc,
7
+ # in spec/support/ and its subdirectories.
8
+ Dir["./spec/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+ config.order = "random"
12
+
13
+ config.include AkivaBrainHelpers
14
+ config.include AkivaStatementsHelpers
15
+
16
+ config.before(:each) do
17
+ TheBigDB.reset_default_configuration
18
+
19
+ TheBigDB.api_host = "fake.test.host"
20
+ # uncomment the following line to show what params are "sent" (obviously nothing is really sent, it's all catched by webmock)
21
+ # TheBigDB.before_request_execution = ->(request){ ap request.data_sent["params"] }
22
+ end
23
+
24
+ config.after(:each) do
25
+ TheBigDB.reset_default_configuration
26
+ end
27
+
28
+ config.filter_run focus: true
29
+ config.run_all_when_everything_filtered = true
30
+ config.filter_run_excluding hidden: true
31
+
32
+ config.expect_with :rspec do |c|
33
+ # c.syntax = :expect
34
+ # c.syntax = :should
35
+ c.syntax = [:should, :expect] # default, enables both `should` and `expect`
36
+ end
37
+ end
@@ -0,0 +1,13 @@
1
+ module AkivaBrainHelpers
2
+
3
+ def reload_core_brain
4
+ Akiva.send(:remove_const, :Brain)
5
+ load File.join(File.dirname(__FILE__), "../../lib/akiva/brain.rb")
6
+
7
+ ["helpers", "filters", "actions", "formatters"].each do |folder|
8
+ Dir[File.join(File.dirname(__FILE__), "../../lib/akiva/core_brain/", folder, "**/*.rb")].each {|file| load file }
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,66 @@
1
+ module AkivaStatementsHelpers
2
+
3
+ def stub_statement(expected_query_sent, expected_statements_received)
4
+ _in = hash_including(expected_query_sent)
5
+ _out = JSON(status: "success", statements: expected_statements_received, total_results: expected_statements_received.size)
6
+ # _out[:total_results] = expected_statements_received.size if expected_statements_received.is_a?(Array)
7
+
8
+ stub_request(:get, /#{TheBigDB.api_host}\/v1\/statements\/search/)
9
+ .with(query: _in)
10
+ .to_return(body: _out)
11
+ end
12
+
13
+ def stub_statements(*ids)
14
+ ids.each do |id|
15
+ statement = STORED_STATEMENTS[id]
16
+ stub_statement(statement[:query], statement[:response])
17
+ end
18
+ end
19
+
20
+ STORED_STATEMENTS = {
21
+ a0: {
22
+ query: {nodes: {subject: "iPhone 5s", property: "weight"}, per_page: "1"},
23
+ response: [{nodes: {subject: "iPhone 5s", property: "weight", answer: "112 grams"}}]
24
+ },
25
+ a1: {
26
+ query: {nodes: {subject: "iPhone 5s", property: "weight and height"}, per_page: "1"},
27
+ response: []
28
+ },
29
+ a2: {
30
+ query: {nodes: {subject: "iPhone 5s", property: "height"}, per_page: "1"},
31
+ response: [{nodes: {subject: "iPhone 5s", property: "height", answer: "123.8 mm"}}]
32
+ },
33
+ a3: {
34
+ query: {nodes: {subject: "iPhone 5s and a Galaxy S4", property: "weight"}, per_page: "1"},
35
+ response: []
36
+ },
37
+ a4: {
38
+ query: {nodes: {subject: "Galaxy S4", property: "weight"}, per_page: "1"},
39
+ response: [{nodes: {subject: "Galaxy S4", property: "weight", answer: "130 grams"}}]
40
+ },
41
+ a5: {
42
+ query: {nodes: {subject: "iPhone 5s and a Galaxy S4", property: "weight and height"}, per_page: "1"},
43
+ response: []
44
+ },
45
+ a6: {
46
+ query: {nodes: {subject: "Galaxy S4", property: "height"}, per_page: "1"},
47
+ response: [{nodes: {subject: "Galaxy S4", property: "height", answer: "136.6 mm"}}]
48
+ },
49
+
50
+ b0: {
51
+ query: {nodes: {subject: "Director of Public Affairs and Communication", property: "salary"}, per_page: "1"},
52
+ response: [{nodes: {subject: "Director of Public Affairs and Communication", property: "salary", answer: "$10,000"}}]
53
+ },
54
+ b1: {
55
+ query: {nodes: {subject: "Director of Public Affairs and Communication", property: "salary and benefits"}, per_page: "1"},
56
+ response: []
57
+ },
58
+ b2: {
59
+ query: {nodes: {subject: "Director of Public Affairs and Communication", property: "benefits"}, per_page: "1"},
60
+ response: [{nodes: {subject: "Director of Public Affairs and Communication", property: "benefits", answer: "Dental"}}]
61
+ },
62
+
63
+
64
+
65
+ }
66
+ end