case_model 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +4 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +61 -0
- data/README.md +27 -0
- data/Rakefile +9 -0
- data/case_model.gemspec +17 -0
- data/lib/case_model.rb +3 -0
- data/lib/descriptions/states.n3 +47 -0
- data/lib/models/action.rb +48 -0
- data/lib/models/answer.rb +25 -0
- data/lib/models/application.rb +57 -0
- data/lib/models/capability.rb +23 -0
- data/lib/models/deployment_plan.rb +44 -0
- data/lib/models/device.rb +26 -0
- data/lib/models/ecosystem_host.rb +5 -0
- data/lib/models/http_request.rb +27 -0
- data/lib/models/location.rb +17 -0
- data/lib/models/ontology.rb +52 -0
- data/lib/models/question.rb +51 -0
- data/lib/models/question_reply_type.rb +14 -0
- data/lib/models/requirement.rb +17 -0
- data/lib/models/selection.rb +32 -0
- data/lib/models/state_change.rb +19 -0
- data/lib/models/temperature.rb +17 -0
- data/lib/ontology/http_vocabulary.rb +17 -0
- data/lib/ontology/local_vocabulary.rb +26 -0
- data/lib/ontology/math_vocabulary.rb +15 -0
- data/lib/ontology/rdf_vocabulary.rb +11 -0
- data/lib/ontology/state_vocabulary.rb +9 -0
- data/lib/services/application_by_capability_finder.rb +37 -0
- data/lib/services/eye_serializer.rb +19 -0
- data/lib/services/node_builder.rb +34 -0
- data/lib/services/node_query.rb +55 -0
- data/lib/services/question_references.rb +19 -0
- data/lib/services/reasoner.rb +77 -0
- data/lib/services/repository_proxy.rb +26 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/unit/models/answer_spec.rb +43 -0
- data/spec/unit/models/application_spec.rb +43 -0
- data/spec/unit/models/deployment_plan_spec.rb +28 -0
- data/spec/unit/models/http_request_spec.rb +42 -0
- data/spec/unit/models/location_spec.rb +36 -0
- data/spec/unit/models/question_spec.rb +89 -0
- data/spec/unit/models/requirement_spec.rb +29 -0
- data/spec/unit/models/selection_spec.rb +48 -0
- data/spec/unit/models/state_change_spec.rb +33 -0
- data/spec/unit/ontology/local_vocabulary_spec.rb +9 -0
- data/spec/unit/ontology/ontology_spec.rb +62 -0
- data/spec/unit/services/application_by_capability_finder_spec.rb +81 -0
- data/spec/unit/services/eye_serializer_spec.rb +34 -0
- data/spec/unit/services/node_builder_spec.rb +56 -0
- data/spec/unit/services/node_query_spec.rb +74 -0
- data/spec/unit/services/reasoner_spec.rb +76 -0
- data/spec/unit/spec_helper.rb +1 -0
- metadata +117 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
class HttpVocabulary
|
2
|
+
def self.method_missing(name, *arguments, &block)
|
3
|
+
uri_for(name)
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.method_name
|
7
|
+
uri_for('methodName')
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.request_uri
|
11
|
+
uri_for('requestURI')
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.uri_for(name)
|
15
|
+
RDF::URI("http://www.w3.org/2011/http##{name}")
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rdf'
|
2
|
+
|
3
|
+
class LocalVocabulary
|
4
|
+
def self.method_missing(name, *arguments, &block)
|
5
|
+
uri_for(name)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.name
|
9
|
+
uri_for('name')
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.question_type
|
13
|
+
uri_for('question')
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.answer_type
|
17
|
+
uri_for('answer')
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.uri_for(name)
|
21
|
+
RDF::URI("http://matus.tomlein.org/case/#{name}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
LV = LocalVocabulary
|
26
|
+
QV = LocalVocabulary
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class MathVocabulary
|
2
|
+
def self.method_missing(name, *arguments, &block)
|
3
|
+
uri_for(name)
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.less_than
|
7
|
+
uri_for('lessThan')
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.uri_for(name)
|
11
|
+
RDF::URI("http://www.w3.org/2000/10/swap/math##{name}")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
MV = MathVocabulary
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class ApplicationByCapabilityFinder
|
2
|
+
def initialize(ontology)
|
3
|
+
@ontology = ontology
|
4
|
+
end
|
5
|
+
|
6
|
+
def find(options)
|
7
|
+
solutions = RDF::Query.execute(@ontology, {
|
8
|
+
application: {
|
9
|
+
LV.hasDeploymentPlan => {
|
10
|
+
LV.capability => options
|
11
|
+
}
|
12
|
+
}
|
13
|
+
})
|
14
|
+
|
15
|
+
solutions.map do |solution|
|
16
|
+
solution[:application]
|
17
|
+
end.uniq.map do |node|
|
18
|
+
Application.new(node, @ontology)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_by_type(type)
|
23
|
+
find({ RDF.type => type })
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_by_description(description)
|
27
|
+
find({ LV.description => description })
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_by_actuator(actuator)
|
31
|
+
find({ LV.actuator => actuator })
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_by_sensor(sensor)
|
35
|
+
find({ LV.sensor => sensor })
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class EyeSerializer
|
2
|
+
def self.serialize_implication(facts, precondition, postcondition)
|
3
|
+
pre = serialize_graph(precondition)
|
4
|
+
post = serialize_graph(postcondition)
|
5
|
+
"#{facts.dump(:ntriples)}{#{pre}} => {#{post}}.\n"
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.serialize_graph(graph)
|
9
|
+
statements = []
|
10
|
+
graph.each_statement do |statement|
|
11
|
+
statements << RDF::Statement.new({
|
12
|
+
subject: statement.subject,
|
13
|
+
predicate: statement.predicate,
|
14
|
+
object: statement.object
|
15
|
+
}).to_s + "\n"
|
16
|
+
end
|
17
|
+
statements.join
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
class NodeBuilder
|
4
|
+
def self.create(properties, repository)
|
5
|
+
node = generate_node
|
6
|
+
query = NodeQuery.new node, repository
|
7
|
+
|
8
|
+
properties.each do |key, v|
|
9
|
+
values = if v.is_a?(Array)
|
10
|
+
v
|
11
|
+
else
|
12
|
+
[ v ]
|
13
|
+
end
|
14
|
+
|
15
|
+
values.each do |value|
|
16
|
+
query.set_value(key,
|
17
|
+
if value.is_a?(Hash)
|
18
|
+
create(value, repository)
|
19
|
+
else
|
20
|
+
value
|
21
|
+
end, false)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
node
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def self.generate_node
|
31
|
+
id = SecureRandom.hex
|
32
|
+
RDF::URI("http://example.org/#{id}")
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class NodeQuery
|
2
|
+
attr_reader :node, :repository
|
3
|
+
|
4
|
+
def initialize(node, repository)
|
5
|
+
@node = node
|
6
|
+
@repository = repository
|
7
|
+
end
|
8
|
+
|
9
|
+
def value(predicate)
|
10
|
+
values(predicate).first
|
11
|
+
end
|
12
|
+
|
13
|
+
def values(predicate)
|
14
|
+
results = repository.query([ node, predicate, nil ])
|
15
|
+
results.map {|s| s.object }
|
16
|
+
end
|
17
|
+
|
18
|
+
def value_exists?(predicate)
|
19
|
+
values(predicate).any?
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_value(predicate, object, replace = true)
|
23
|
+
delete_value(predicate) if replace
|
24
|
+
@repository << [ node, predicate, object ]
|
25
|
+
end
|
26
|
+
|
27
|
+
def predicates_and_objects
|
28
|
+
values = {}
|
29
|
+
repository.query([ node, nil, nil ]).each do |s|
|
30
|
+
if values.has_key? s.predicate
|
31
|
+
if values[s.predicate].is_a? Array
|
32
|
+
values[s.predicate] << s.object
|
33
|
+
else
|
34
|
+
values[s.predicate] = [
|
35
|
+
values[s.predicate], s.object
|
36
|
+
]
|
37
|
+
end
|
38
|
+
else
|
39
|
+
values[s.predicate] = s.object
|
40
|
+
end
|
41
|
+
end
|
42
|
+
values
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def delete_value(predicate)
|
48
|
+
# makes sure not to delete any values in subgraphs
|
49
|
+
repository.query([ node, predicate, nil ]).select do |s|
|
50
|
+
s.graph_name.nil?
|
51
|
+
end.each do |s|
|
52
|
+
repository.delete(s)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module QuestionReferences
|
2
|
+
def has_question?
|
3
|
+
query.value_exists?(LV.requires_answering)
|
4
|
+
end
|
5
|
+
|
6
|
+
def has_condition?
|
7
|
+
query.value_exists?(LV.condition)
|
8
|
+
end
|
9
|
+
|
10
|
+
def question
|
11
|
+
node = query.value(LV.requires_answering)
|
12
|
+
return nil if node.nil?
|
13
|
+
Question.new(node, query.repository)
|
14
|
+
end
|
15
|
+
|
16
|
+
def question_answered?
|
17
|
+
question.has_answer?
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'rdf/n3'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
class Reasoner
|
5
|
+
attr_reader :repository
|
6
|
+
|
7
|
+
def initialize(repository)
|
8
|
+
@repository = repository
|
9
|
+
end
|
10
|
+
|
11
|
+
# implication doesn't work well when a blank node is used in the
|
12
|
+
# condition, e.g.:
|
13
|
+
# {?device1 <http://...#type> _:vehicle .} => {...}
|
14
|
+
# will always result in the postcondition, because of _:vehicle
|
15
|
+
def imply(precondition, postcondition)
|
16
|
+
eye_input = EyeSerializer.serialize_implication(
|
17
|
+
repository.facts_only,
|
18
|
+
repository.graph(precondition),
|
19
|
+
repository.graph(postcondition)
|
20
|
+
)
|
21
|
+
|
22
|
+
eye_output = run_eye eye_input
|
23
|
+
parse_eye_output eye_output
|
24
|
+
end
|
25
|
+
|
26
|
+
# makes sure that the graph with name condition is valid
|
27
|
+
# to do that, it creates a temporary graph (:helper_graph) and tries to imply
|
28
|
+
# condition => helper_graph
|
29
|
+
# it creates a new temp_reasoner object so as not to modify the current repository
|
30
|
+
# with the helper_graph
|
31
|
+
def check_condition(condition)
|
32
|
+
temp_repository = @repository.clone
|
33
|
+
temp_repository << [ LV.condition, LV.was, LV.true, :helper_graph ]
|
34
|
+
|
35
|
+
temp_reasoner = Reasoner.new temp_repository
|
36
|
+
result = temp_reasoner.imply(condition, :helper_graph)
|
37
|
+
|
38
|
+
result.query([ LV.condition, LV.was, LV.true ]).any?
|
39
|
+
end
|
40
|
+
|
41
|
+
def load_and_process_n3(n3_input)
|
42
|
+
input = [
|
43
|
+
n3_input,
|
44
|
+
EyeSerializer.serialize_graph(repository.facts_only)
|
45
|
+
].join
|
46
|
+
|
47
|
+
eye_output = run_eye input
|
48
|
+
parse_eye_output eye_output, @repository
|
49
|
+
@repository
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def run_eye(input)
|
55
|
+
data = Tempfile.new('data')
|
56
|
+
data.write(input)
|
57
|
+
data.close
|
58
|
+
output = `eye --nope --pass #{data.path} 2> /dev/null`
|
59
|
+
data.unlink
|
60
|
+
|
61
|
+
output
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_eye_output(output, repo = nil)
|
65
|
+
output = remove_incompatible_prefix_from_eye_output output
|
66
|
+
repo = RDF::Repository.new if repo.nil?
|
67
|
+
reader = RDF::Reader.for(:n3).new(output)
|
68
|
+
reader.each_statement { |statement| repo << statement }
|
69
|
+
repo
|
70
|
+
end
|
71
|
+
|
72
|
+
def remove_incompatible_prefix_from_eye_output(output)
|
73
|
+
output.gsub(/PREFIX .+\n/) do |match|
|
74
|
+
match.sub('PREFIX ', '@prefix ') + '.'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RepositoryProxy
|
2
|
+
def graph(name)
|
3
|
+
graph_name = if name.is_a? Symbol; "_:#{name.to_s}"; else; name.to_s; end
|
4
|
+
g = RDF::Graph.new
|
5
|
+
repository.statements.each do |statement|
|
6
|
+
next if statement.graph_name.nil?
|
7
|
+
next unless statement.graph_name.to_s.include? graph_name
|
8
|
+
|
9
|
+
g << [ statement.subject, statement.predicate, statement.object ]
|
10
|
+
end
|
11
|
+
g
|
12
|
+
end
|
13
|
+
|
14
|
+
def facts_only
|
15
|
+
RDF::Graph.new do |g|
|
16
|
+
repository.statements.each do |statement|
|
17
|
+
g << statement if statement.graph_name.nil?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# proxy everything to the repository
|
23
|
+
def method_missing(method, *args, &block)
|
24
|
+
repository.send(method, *args, &block)
|
25
|
+
end
|
26
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Answer do
|
4
|
+
let(:repo) do
|
5
|
+
RDF::Repository.new do |r|
|
6
|
+
# DEVICE
|
7
|
+
r << [:dev, RDF.type, LV.Device]
|
8
|
+
r << [:dev, LV.manufacturerName, 'Grundfos']
|
9
|
+
r << [:dev, LV.locatedAt, :device_location]
|
10
|
+
# QUESTION
|
11
|
+
r << [:device_location, RDF.type, QV.question_type]
|
12
|
+
r << [:device_location, QV.replyType, QV.Location]
|
13
|
+
r << [:device_location, QV.location_of, :dev]
|
14
|
+
r << [:device_location, QV.text, 'Where is the thing?']
|
15
|
+
# ANSWER
|
16
|
+
r << [:device_location, QV.has_answer, :answer]
|
17
|
+
r << [:answer, RDF.type, QV.answer_type]
|
18
|
+
r << [:answer, QV.answers, :device_location]
|
19
|
+
r << [:answer, RDF.type, QV.Location]
|
20
|
+
r << [:answer, LV.locationName, 'Living Room']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:ontology) { Ontology.new(repo) }
|
25
|
+
let(:question) { Question.new(:device_location, ontology) }
|
26
|
+
let(:answer) { question.answer }
|
27
|
+
|
28
|
+
context '#question.text' do
|
29
|
+
subject { answer.question.text }
|
30
|
+
it { is_expected.not_to eq(nil) }
|
31
|
+
it { is_expected.to eq('Where is the thing?') }
|
32
|
+
end
|
33
|
+
|
34
|
+
context '#reply_type' do
|
35
|
+
subject { answer.reply_type }
|
36
|
+
it { is_expected.to eq(:location) }
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'should have type Location' do
|
40
|
+
subject { answer }
|
41
|
+
it { is_expected.to be_kind_of Location }
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
def create_repo
|
4
|
+
RDF::Repository.new do |r|
|
5
|
+
r << [:a, RDF.type, LV.Application]
|
6
|
+
r << [:a, RDF::Vocab::FOAF.name, 'Some App']
|
7
|
+
r << [:a, LV.description, 'Description of the app.']
|
8
|
+
|
9
|
+
r << [:a, LV.hasDeploymentPlan, :deployment_plan1]
|
10
|
+
r << [:a, LV.hasDeploymentPlan, :deployment_plan2]
|
11
|
+
r << [:deployment_plan1, LV.description, 'This needs to be so and so.']
|
12
|
+
r << [:deployment_plan2, LV.description, 'This needs to be so and so.']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe Application do
|
17
|
+
let(:repo) { create_repo }
|
18
|
+
let(:ontology) { Ontology.new(repo) }
|
19
|
+
let(:app) { Application.new(:a, repo) }
|
20
|
+
|
21
|
+
context '#name' do
|
22
|
+
subject { app.name }
|
23
|
+
it { is_expected.to eq('Some App') }
|
24
|
+
end
|
25
|
+
|
26
|
+
context '#description' do
|
27
|
+
subject { app.description }
|
28
|
+
it { is_expected.to eq('Description of the app.') }
|
29
|
+
end
|
30
|
+
|
31
|
+
context '#deployment_plans' do
|
32
|
+
subject { app.deployment_plans.size }
|
33
|
+
it { is_expected.to eq 2 }
|
34
|
+
|
35
|
+
describe 'should not have doubles' do
|
36
|
+
before do
|
37
|
+
repo << [ :a, LV.hasDeploymentPlan, :deployment_plan2 ]
|
38
|
+
end
|
39
|
+
|
40
|
+
it { is_expected.to eq 2 }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe DeploymentPlan do
|
4
|
+
let(:repo) { RDF::Repository.new }
|
5
|
+
let(:ontology) { Ontology.new(repo) }
|
6
|
+
let(:deployment_plan) { DeploymentPlan.new(:a, repo) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
repo << [:deployment_plan, LV.description, 'Install the app']
|
10
|
+
end
|
11
|
+
|
12
|
+
context '#description' do
|
13
|
+
subject { deployment_plan.description }
|
14
|
+
it { is_expected.to eq('Install the app') }
|
15
|
+
end
|
16
|
+
|
17
|
+
context '#arguments' do
|
18
|
+
before do
|
19
|
+
arguments = RDF::List[ 'arg1', 'arg2' ]
|
20
|
+
repo << arguments
|
21
|
+
repo << [ :deployment_plan, LV.arguments, arguments ]
|
22
|
+
end
|
23
|
+
let(:arguments) { deployment_plan.arguments }
|
24
|
+
|
25
|
+
subject { arguments.to_a }
|
26
|
+
it { is_expected.to eq [ 'arg1', 'arg2' ] }
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe HttpRequest do
|
4
|
+
let(:repo) { RDF::Repository.new }
|
5
|
+
let(:ontology) { Ontology.new(repo) }
|
6
|
+
let(:request) { HttpRequest.new(:r, repo) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
repo << [ :r, HttpVocabulary.method_name, 'POST' ]
|
10
|
+
repo << [ :r, HttpVocabulary.body, 'the body' ]
|
11
|
+
end
|
12
|
+
|
13
|
+
context '#uri' do
|
14
|
+
subject { request.uri }
|
15
|
+
|
16
|
+
describe 'as literal' do
|
17
|
+
before { repo << [ :r, HttpVocabulary.request_uri, 'http://example.com' ] }
|
18
|
+
|
19
|
+
it { is_expected.to eq 'http://example.com' }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'as URI' do
|
23
|
+
before { repo << [ :r, HttpVocabulary.request_uri, RDF::URI('http://example.eu') ] }
|
24
|
+
|
25
|
+
it { is_expected.to eq 'http://example.eu' }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context '#execute' do
|
30
|
+
before do
|
31
|
+
repo << [ :r, HttpVocabulary.request_uri, 'http://example.com' ]
|
32
|
+
stub_request(:post, 'example.com')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'makes the request' do
|
36
|
+
expect(request.execute).to eq true
|
37
|
+
|
38
|
+
expect(WebMock).to have_requested(:post, 'example.com').
|
39
|
+
with(:body => 'the body')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Location do
|
4
|
+
let(:repo) do
|
5
|
+
RDF::Repository.new do |r|
|
6
|
+
# ANSWER
|
7
|
+
r << [:location, RDF.type, QV.answer_type]
|
8
|
+
r << [:location, RDF.type, QV.location]
|
9
|
+
r << [:location, LV.locationName, 'Living Room']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:ontology) { Ontology.new(repo) }
|
14
|
+
let(:location) { Location.new(:location, repo) }
|
15
|
+
|
16
|
+
context '#name' do
|
17
|
+
subject { location.name }
|
18
|
+
it { is_expected.to eq('Living Room') }
|
19
|
+
end
|
20
|
+
|
21
|
+
context '#name=' do
|
22
|
+
before :each do
|
23
|
+
location.name = 'Kitchen'
|
24
|
+
end
|
25
|
+
|
26
|
+
subject { location.name }
|
27
|
+
it { is_expected.to eq('Kitchen') }
|
28
|
+
|
29
|
+
describe 'new instances should have the changed value' do
|
30
|
+
let(:new_answer) { Location.new(:location, repo) }
|
31
|
+
|
32
|
+
subject { new_answer.name }
|
33
|
+
it { is_expected.to eq('Kitchen') }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Question do
|
4
|
+
let(:repo) do
|
5
|
+
RDF::Repository.new do |r|
|
6
|
+
r << [:a, RDF.type, LV.Application]
|
7
|
+
r << [:device_location, RDF.type, QV.question_type]
|
8
|
+
r << [:device_location, QV.text, 'Where is the thing?']
|
9
|
+
r << [:device_location, QV.replyType, QV.Location]
|
10
|
+
r << [:device_location, QV.location_of, :device]
|
11
|
+
r << [:dev, RDF.type, LV.Device]
|
12
|
+
r << [:dev, LV.manufacturerName, 'Apple']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:ontology) { Ontology.new(repo) }
|
17
|
+
let(:question) { Question.new(:device_location, repo) }
|
18
|
+
|
19
|
+
context '#text' do
|
20
|
+
subject { question.text }
|
21
|
+
it { is_expected.to eq('Where is the thing?') }
|
22
|
+
end
|
23
|
+
|
24
|
+
context '#reply_type' do
|
25
|
+
subject { question.reply_type }
|
26
|
+
it { is_expected.to eq(:location) }
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'without answer' do
|
30
|
+
context '#has_answer?' do
|
31
|
+
subject { question.has_answer? }
|
32
|
+
it { is_expected.to eq(false) }
|
33
|
+
end
|
34
|
+
|
35
|
+
context '#answer' do
|
36
|
+
subject { question.answer }
|
37
|
+
it { is_expected.to eq(nil) }
|
38
|
+
end
|
39
|
+
|
40
|
+
context '#answer!' do
|
41
|
+
let(:answer) { question.answer! }
|
42
|
+
|
43
|
+
subject { answer }
|
44
|
+
it { is_expected.not_to eq(nil) }
|
45
|
+
|
46
|
+
describe 'location answer properties' do
|
47
|
+
before :each do
|
48
|
+
answer.name = 'Balcony'
|
49
|
+
end
|
50
|
+
|
51
|
+
subject { answer.name }
|
52
|
+
it { is_expected.to eq('Balcony') }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'with answer' do
|
58
|
+
before :each do
|
59
|
+
repo << [:answer, RDF.type, QV.answer_type]
|
60
|
+
repo << [:answer, QV.answers, :device_location]
|
61
|
+
repo << [:device_location, QV.has_answer, :answer]
|
62
|
+
end
|
63
|
+
|
64
|
+
context '#has_answer?' do
|
65
|
+
subject { question.has_answer? }
|
66
|
+
it { is_expected.to eq(true) }
|
67
|
+
end
|
68
|
+
|
69
|
+
context '#answer' do
|
70
|
+
let(:answer) { question.answer }
|
71
|
+
subject { answer }
|
72
|
+
|
73
|
+
it { is_expected.not_to eq(nil) }
|
74
|
+
|
75
|
+
context '#question.text' do
|
76
|
+
subject { answer.question.text }
|
77
|
+
it { is_expected.to eq(question.text) }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context '#answer!' do
|
82
|
+
it 'should have the same value as calling answer' do
|
83
|
+
with_bang = question.answer!.name
|
84
|
+
without_bang = question.answer.name
|
85
|
+
expect(with_bang).to eq(without_bang)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Ontology do
|
4
|
+
let(:repo) { RDF::Repository.new }
|
5
|
+
let(:ontology) { Ontology.new(repo) }
|
6
|
+
let(:requirement) { Requirement.new(:req, repo) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
repo << [ :req, LV.description, 'This needs to be so and so.' ]
|
10
|
+
end
|
11
|
+
|
12
|
+
context '#description' do
|
13
|
+
subject { requirement.description }
|
14
|
+
it { is_expected.to eq('This needs to be so and so.') }
|
15
|
+
end
|
16
|
+
|
17
|
+
context '#satisfied?' do
|
18
|
+
describe 'satisfied requirement' do
|
19
|
+
before { repo << [ :req, LV.satisfied, true ] }
|
20
|
+
subject { requirement.satisfied? }
|
21
|
+
it { is_expected.to eq true }
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'not satisfied requirement' do
|
25
|
+
subject { requirement.satisfied? }
|
26
|
+
it { is_expected.to eq false }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|