case_model 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +11 -0
  4. data/Gemfile.lock +61 -0
  5. data/README.md +27 -0
  6. data/Rakefile +9 -0
  7. data/case_model.gemspec +17 -0
  8. data/lib/case_model.rb +3 -0
  9. data/lib/descriptions/states.n3 +47 -0
  10. data/lib/models/action.rb +48 -0
  11. data/lib/models/answer.rb +25 -0
  12. data/lib/models/application.rb +57 -0
  13. data/lib/models/capability.rb +23 -0
  14. data/lib/models/deployment_plan.rb +44 -0
  15. data/lib/models/device.rb +26 -0
  16. data/lib/models/ecosystem_host.rb +5 -0
  17. data/lib/models/http_request.rb +27 -0
  18. data/lib/models/location.rb +17 -0
  19. data/lib/models/ontology.rb +52 -0
  20. data/lib/models/question.rb +51 -0
  21. data/lib/models/question_reply_type.rb +14 -0
  22. data/lib/models/requirement.rb +17 -0
  23. data/lib/models/selection.rb +32 -0
  24. data/lib/models/state_change.rb +19 -0
  25. data/lib/models/temperature.rb +17 -0
  26. data/lib/ontology/http_vocabulary.rb +17 -0
  27. data/lib/ontology/local_vocabulary.rb +26 -0
  28. data/lib/ontology/math_vocabulary.rb +15 -0
  29. data/lib/ontology/rdf_vocabulary.rb +11 -0
  30. data/lib/ontology/state_vocabulary.rb +9 -0
  31. data/lib/services/application_by_capability_finder.rb +37 -0
  32. data/lib/services/eye_serializer.rb +19 -0
  33. data/lib/services/node_builder.rb +34 -0
  34. data/lib/services/node_query.rb +55 -0
  35. data/lib/services/question_references.rb +19 -0
  36. data/lib/services/reasoner.rb +77 -0
  37. data/lib/services/repository_proxy.rb +26 -0
  38. data/spec/spec_helper.rb +8 -0
  39. data/spec/unit/models/answer_spec.rb +43 -0
  40. data/spec/unit/models/application_spec.rb +43 -0
  41. data/spec/unit/models/deployment_plan_spec.rb +28 -0
  42. data/spec/unit/models/http_request_spec.rb +42 -0
  43. data/spec/unit/models/location_spec.rb +36 -0
  44. data/spec/unit/models/question_spec.rb +89 -0
  45. data/spec/unit/models/requirement_spec.rb +29 -0
  46. data/spec/unit/models/selection_spec.rb +48 -0
  47. data/spec/unit/models/state_change_spec.rb +33 -0
  48. data/spec/unit/ontology/local_vocabulary_spec.rb +9 -0
  49. data/spec/unit/ontology/ontology_spec.rb +62 -0
  50. data/spec/unit/services/application_by_capability_finder_spec.rb +81 -0
  51. data/spec/unit/services/eye_serializer_spec.rb +34 -0
  52. data/spec/unit/services/node_builder_spec.rb +56 -0
  53. data/spec/unit/services/node_query_spec.rb +74 -0
  54. data/spec/unit/services/reasoner_spec.rb +76 -0
  55. data/spec/unit/spec_helper.rb +1 -0
  56. metadata +117 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7975b117f28b3b97b7280d00cfa2ed0ed2ed5f88
4
+ data.tar.gz: e6afbc109997f137b98fa31d54041a1f51bd0b97
5
+ SHA512:
6
+ metadata.gz: db40192af1d3e44978dce73891cca821f5a00cff05b1d6b42a029afb2e3c4d7728f14920ca68f767e1111742faa99826ff7100a6b7fbbc171f4c785a636bde61
7
+ data.tar.gz: 04bbf2ca4ba64e812318d6b1b20a79210baf416ce8d3872d27eb8107af0f6e72498274dc40af6d46e6cace603901ad22a792eb38ea85bdc5802383579e82bc71
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.1.1"
4
+ - rbx
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ # gem "rubidium", path: "~/Dropbox/PhD/project/code/rubidium"
4
+ # gem "rubidium-dir", path: "~/Dropbox/PhD/project/code/rubidium-dir"
5
+
6
+ gem 'rdf-n3', '1.99.0'
7
+ gem 'rdf-vocab', '0.8.7.1'
8
+ gem 'webmock', '1.22.3'
9
+ gem 'rest-client', '1.8.0'
10
+ gem 'rake', '10.5.0'
11
+ gem 'rspec', '3.4.0'
data/Gemfile.lock ADDED
@@ -0,0 +1,61 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.4.0)
5
+ crack (0.4.3)
6
+ safe_yaml (~> 1.0.0)
7
+ diff-lcs (1.2.5)
8
+ domain_name (0.5.25)
9
+ unf (>= 0.0.5, < 1.0.0)
10
+ hashdiff (0.2.3)
11
+ http-cookie (1.0.2)
12
+ domain_name (~> 0.5)
13
+ link_header (0.0.8)
14
+ mime-types (2.99)
15
+ netrc (0.11.0)
16
+ rake (10.5.0)
17
+ rdf (1.99.1)
18
+ link_header (~> 0.0, >= 0.0.8)
19
+ rdf-n3 (1.99.0)
20
+ rdf (~> 1.99)
21
+ rdf-vocab (0.8.7.1)
22
+ rdf (~> 1.1, >= 1.1.10)
23
+ rest-client (1.8.0)
24
+ http-cookie (>= 1.0.2, < 2.0)
25
+ mime-types (>= 1.16, < 3.0)
26
+ netrc (~> 0.7)
27
+ rspec (3.4.0)
28
+ rspec-core (~> 3.4.0)
29
+ rspec-expectations (~> 3.4.0)
30
+ rspec-mocks (~> 3.4.0)
31
+ rspec-core (3.4.1)
32
+ rspec-support (~> 3.4.0)
33
+ rspec-expectations (3.4.0)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.4.0)
36
+ rspec-mocks (3.4.0)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.4.0)
39
+ rspec-support (3.4.1)
40
+ safe_yaml (1.0.4)
41
+ unf (0.1.4)
42
+ unf_ext
43
+ unf_ext (0.0.7.1)
44
+ webmock (1.22.3)
45
+ addressable (>= 2.3.6)
46
+ crack (>= 0.3.2)
47
+ hashdiff
48
+
49
+ PLATFORMS
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ rake (= 10.5.0)
54
+ rdf-n3 (= 1.99.0)
55
+ rdf-vocab (= 0.8.7.1)
56
+ rest-client (= 1.8.0)
57
+ rspec (= 3.4.0)
58
+ webmock (= 1.22.3)
59
+
60
+ BUNDLED WITH
61
+ 1.11.2
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ CASE (Context-aware Software Ecosystem)
2
+ =======================================
3
+
4
+ Evaluation of application models
5
+ --------------------------------
6
+
7
+ Supplementary evaluation for the paper **Semantic Model of Variability and Capabilities of IoT Applications for Embedded Software Ecosystems**.
8
+
9
+ # Browsing the sample scenarios
10
+
11
+ To investigate the tested application models and scenarios, see the folder
12
+ [spec/use_cases](spec/use_cases).
13
+
14
+ # Installation
15
+
16
+ 1. Install the Eye reasoner
17
+ - ([Windows](http://eulersharp.sourceforge.net/README.Windows), [OS X](http://eulersharp.sourceforge.net/README.MacOSX), [Linux](http://eulersharp.sourceforge.net/README.Linux))
18
+ - requires the SWI Prolog
19
+ - `brew tap homebrew/x11`
20
+ - `brew install swi-prolog`
21
+ 2. Install [Ruby](https://www.ruby-lang.org/en/) version 2.x.x
22
+ 3. Run in inside a folder for this repository: `gem install bundler`
23
+ 4. Run in inside a folder for this repository: `bundle install`
24
+
25
+ # Running the specs
26
+
27
+ To run the tests, simply execute `rspec` in the folder of this repository.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+ rescue LoadError
8
+ # no rspec available
9
+ end
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'case_model'
3
+ s.version = '0.0.1'
4
+ s.date = '2016-01-28'
5
+ s.summary = 'Resolver for the CASE ecosystem application models'
6
+ s.description = 'Built for the Context-Aware Software Ecosystem. Resolves application model requirements using semantic reasoning.'
7
+ s.authors = ['Matus Tomlein']
8
+ s.email = 'matus@cs.au.dk'
9
+
10
+ s.files = `git ls-files -z`.split("\x0")
11
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
12
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
13
+ s.require_paths = ["lib"]
14
+
15
+ s.homepage = 'https://github.com/case-iot/app_model'
16
+ s.license = 'MIT'
17
+ end
data/lib/case_model.rb ADDED
@@ -0,0 +1,3 @@
1
+ Dir[File.join(File.dirname(__FILE__), "**/*.rb")].each do |f|
2
+ require f
3
+ end
@@ -0,0 +1,47 @@
1
+ @prefix e: <http://eulersharp.sourceforge.net/2003/03swap/log-rules#>.
2
+ @prefix st: <http://purl.org/restdesc/states#>.
3
+ @prefix log: <http://www.w3.org/2000/10/swap/log#>.
4
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
5
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
6
+ @prefix owl: <http://www.w3.org/2002/07/owl#>.
7
+
8
+ st:State a rdfs:Class;
9
+ rdfs:label "A consistent state of (parts of) the world at a certain point in time."@en.
10
+
11
+ st:StateChange a rdfs:Class;
12
+ rdfs:label "A transition from one state to another."@en.
13
+
14
+ st:added a rdf:Property, owl:FunctionalProperty;
15
+ rdfs:label "added triples"@en;
16
+ rdfs:domain st:StateChange;
17
+ rdfs:range log:Formula.
18
+
19
+ st:removed a rdf:Property, owl:FunctionalProperty;
20
+ rdfs:label "removed triples"@en;
21
+ rdfs:domain st:StateChange;
22
+ rdfs:range log:Formula.
23
+
24
+ st:parent a rdf:Property, owl:FunctionalProperty;
25
+ rdfs:label "is based upon"@en;
26
+ rdfs:domain st:StateChange;
27
+ rdfs:range st:State.
28
+
29
+ st:derivedFrom a rdf:Property, owl:FunctionalProperty;
30
+ rdfs:label "is derived from"@en;
31
+ rdfs:domain st:State;
32
+ rdfs:range st:StateChange.
33
+
34
+ # Derive the complete state from a state change
35
+ {
36
+ ?change st:added ?added;
37
+ st:removed ?removed;
38
+ st:parent ?parent.
39
+
40
+ (?parent ?removed) e:graphDifference ?inherited.
41
+ (?inherited ?added) log:conjunction ?newState.
42
+ }
43
+ =>
44
+ {
45
+ ?newState a st:State;
46
+ st:derivedFrom ?change.
47
+ }.
@@ -0,0 +1,48 @@
1
+ require_relative '../services/question_references'
2
+
3
+ class Action
4
+ attr_reader :query
5
+
6
+ include QuestionReferences
7
+
8
+ def initialize(node, repository)
9
+ @query = NodeQuery.new(node, repository)
10
+ end
11
+
12
+ def description
13
+ query.value(LV.description)
14
+ end
15
+
16
+ def requirements
17
+ list = RDF::List.new(query.value(LV.requirement), query.repository)
18
+ list.map { |node| Requirement.new(node, query.repository) }
19
+ end
20
+
21
+ def failed_requirements
22
+ requirements.select {|r| not r.satisfied? }
23
+ end
24
+
25
+ def compatible?
26
+ not failed_requirements.any?
27
+ end
28
+
29
+ def questions
30
+ q = []
31
+ q << question if has_question?
32
+
33
+ with_question = requirements.select { |r| r.has_question? }
34
+ q + with_question.map { |r| r.question }
35
+ end
36
+
37
+ def open_questions
38
+ questions.select { |q| not q.has_answer? }
39
+ end
40
+
41
+ def execution
42
+ ActionExecution.new(query.value(LV.executes), query.repository)
43
+ end
44
+
45
+ def effect
46
+ StateChange.new(query.value(LV.effect), query.repository)
47
+ end
48
+ end
@@ -0,0 +1,25 @@
1
+ module Answer
2
+ def self.create(reply_type, node, repository)
3
+ case reply_type
4
+ when :location
5
+ Location.new(node, repository)
6
+ when :temperature
7
+ Temperature.new(node, repository)
8
+ when :select
9
+ Selection.new(node, repository)
10
+ else
11
+ nil
12
+ end
13
+ end
14
+
15
+ def reply_type
16
+ question.reply_type
17
+ end
18
+
19
+ def question
20
+ node = query.value(QV.answers)
21
+ return nil if node.nil?
22
+
23
+ Question.new(node, query.repository)
24
+ end
25
+ end
@@ -0,0 +1,57 @@
1
+ class Application
2
+ attr_reader :query
3
+
4
+ def initialize(node, repository)
5
+ @query = NodeQuery.new(node, repository)
6
+ end
7
+
8
+ def name
9
+ if query.value_exists?(RDF::Vocab::FOAF.name)
10
+ query.value(RDF::Vocab::FOAF.name)
11
+ else
12
+ query.value(LV.name)
13
+ end
14
+ end
15
+
16
+ def description
17
+ query.value(LV.description)
18
+ end
19
+
20
+ def questions
21
+ list = repo.query([nil, RDF.type, QV.Question]).map { |s| s.subject }
22
+ list.map { |node| Question.new(node, repo) }
23
+ end
24
+
25
+ def open_questions
26
+ questions.select { |q| not q.has_answer? }
27
+ end
28
+
29
+ def question_with_text(text)
30
+ questions.find { |q| q.text == text }
31
+ end
32
+
33
+ def requirements
34
+ list = repo.query([nil, RDF.type, QV.Requirement]).map { |s| s.subject }
35
+ list.map { |node| Requirement.new(node, repo) }
36
+ end
37
+
38
+ def requirement_with_description(description)
39
+ requirements.find { |r| r.description == description }
40
+ end
41
+
42
+ def deployment_plans
43
+ query.values(LV.hasDeploymentPlan).map do |node|
44
+ DeploymentPlan.new(node, repo)
45
+ end
46
+ end
47
+
48
+ def deployment_plan_with_description(description)
49
+ deployment_plans.find { |a| a.description == description }
50
+ end
51
+
52
+ private
53
+
54
+ def repo
55
+ query.repository
56
+ end
57
+ end
@@ -0,0 +1,23 @@
1
+ class Capability
2
+ attr_reader :query
3
+
4
+ def initialize(node, repository)
5
+ @query = NodeQuery.new(node, repository)
6
+ end
7
+
8
+ def description
9
+ query.value(LV.description)
10
+ end
11
+
12
+ def actuators
13
+ query.values(LV.actuator).map do |node|
14
+ Device.new node, query.repository
15
+ end
16
+ end
17
+
18
+ def sensors
19
+ query.values(LV.sensor).map do |node|
20
+ Device.new node, query.repository
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../services/question_references'
2
+
3
+ class DeploymentPlan
4
+ attr_reader :query
5
+
6
+ include QuestionReferences
7
+
8
+ def initialize(node, repository)
9
+ @query = NodeQuery.new(node, repository)
10
+ end
11
+
12
+ def description
13
+ query.value(LV.description)
14
+ end
15
+
16
+ def host
17
+ nil unless query.value_exists?(LV.host)
18
+ EcosystemHost.new(query.value(LV.host), query.repository)
19
+ end
20
+
21
+ def arguments
22
+ node = query.value(LV.arguments)
23
+ arg_query = NodeQuery.new(node, query.repository)
24
+
25
+ if arg_query.value_exists?(RDFV.first)
26
+ RDF::List.new(node, query.repository)
27
+ else
28
+ arg_query.predicates_and_objects
29
+ end
30
+ end
31
+
32
+ def deployment_plans
33
+ query.values(LV.hasDeploymentPlan).map do |node|
34
+ DeploymentPlan.new(node, query.repository)
35
+ end
36
+ end
37
+
38
+ def capabilities
39
+ list = RDF::List.new(query.value(LV.capabilities), query.repository)
40
+ list.map do |node|
41
+ Capability.new(node, query.repository)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,26 @@
1
+ class Device
2
+ attr_reader :query
3
+
4
+ def initialize(node, repository)
5
+ @query = NodeQuery.new(node, repository)
6
+ end
7
+
8
+ def manufacturer_name
9
+ query.value(LV.manufacturerName)
10
+ end
11
+
12
+ def description
13
+ query.value(LV.description)
14
+ end
15
+
16
+ def location
17
+ node = query.value(LV.locatedAt)
18
+ return nil if node.nil?
19
+
20
+ Location.new(node, query.repository)
21
+ end
22
+
23
+ def node
24
+ query.node
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ class EcosystemHost < Device
2
+ def initialize(node, repository)
3
+ super(node, repository)
4
+ end
5
+ end
@@ -0,0 +1,27 @@
1
+ require 'rest-client'
2
+
3
+ class HttpRequest
4
+ attr_reader :query
5
+
6
+ def initialize(node, repository)
7
+ @query = NodeQuery.new(node, repository)
8
+ end
9
+
10
+ def uri
11
+ query.value(HttpVocabulary.request_uri)
12
+ end
13
+
14
+ def body
15
+ query.value(HttpVocabulary.body)
16
+ end
17
+
18
+ def method_name
19
+ query.value(HttpVocabulary.method_name)
20
+ end
21
+
22
+ def execute
23
+ raise "Unsupported method #{method_name}" unless method_name == 'POST'
24
+ RestClient.post(uri.to_s, body.to_s)
25
+ true
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ class Location
2
+ attr_reader :query
3
+
4
+ include Answer
5
+
6
+ def initialize(node, repository)
7
+ @query = NodeQuery.new(node, repository)
8
+ end
9
+
10
+ def name
11
+ query.value(LV.locationName)
12
+ end
13
+
14
+ def name=(value)
15
+ query.set_value(LV.locationName, value)
16
+ end
17
+ end
@@ -0,0 +1,52 @@
1
+ require 'rdf'
2
+ require 'rdf/vocab'
3
+
4
+ require_relative '../services/repository_proxy'
5
+
6
+ class Ontology
7
+ attr_reader :repository
8
+
9
+ include RepositoryProxy
10
+
11
+ def self.from_n3(n3_input)
12
+ Ontology.new(RDF::Repository.new, n3_input)
13
+ end
14
+
15
+ def initialize(repository, n3_input = nil)
16
+ @repository = repository
17
+ load_and_process_n3 n3_input if n3_input
18
+ # initialize_state
19
+ end
20
+
21
+ def load_and_process_n3(n3_input)
22
+ (@n3_inputs ||= []) << n3_input
23
+ refresh
24
+ end
25
+
26
+ def applications
27
+ query([nil, RDF.type, LV.Application]).map do |statement|
28
+ Application.new(statement.subject, self)
29
+ end
30
+ end
31
+
32
+ def devices
33
+ query([nil, RDF.type, LV.Device]).map do |statement|
34
+ Device.new(statement.subject, self)
35
+ end
36
+ end
37
+
38
+ def device_with_description(description)
39
+ devices.find { |d| d.description == description }
40
+ end
41
+
42
+ def refresh
43
+ @n3_inputs.each { |input| Reasoner.new(self).load_and_process_n3(input) }
44
+ end
45
+
46
+ private
47
+
48
+ ## load an empty State formula in case there are some StateChange statements
49
+ def initialize_state
50
+ load_and_process_n3 '{ <http://example.com/x> <http://example.com/y> <http://example.com/z>.} a <http://purl.org/restdesc/states#State>.'
51
+ end
52
+ end
@@ -0,0 +1,51 @@
1
+ class Question
2
+ attr_reader :query
3
+
4
+ def initialize(node, repository)
5
+ @query = NodeQuery.new(node, repository)
6
+ end
7
+
8
+ def text
9
+ query.value(QV.text).to_s
10
+ end
11
+
12
+ def reply_type
13
+ QuestionReplyType.to_sym(query)
14
+ end
15
+
16
+ def has_answer?
17
+ query.value_exists?(QV.has_answer)
18
+ end
19
+
20
+ # returns an existing one or nil if there is no
21
+ def answer
22
+ node = query.value(QV.has_answer)
23
+ return nil if node.nil?
24
+
25
+ Answer.create(reply_type, node, query.repository)
26
+ end
27
+
28
+ # creates a new answer or returns an existing one
29
+ def answer!
30
+ answer || create_answer
31
+ end
32
+
33
+ private
34
+
35
+ def repository; query.repository; end
36
+
37
+ def create_answer
38
+ node = generate_answer_node
39
+ repository << [ node, RDF.type, QV.answer_type ]
40
+ answer_query = NodeQuery.new(node, repository)
41
+ answer_query.set_value(QV.answers, query.node)
42
+ query.set_value(QV.has_answer, node)
43
+
44
+ answer
45
+ end
46
+
47
+ def generate_answer_node
48
+ question_uri_hash = query.node.to_s.hash.to_s
49
+ ('answer' + question_uri_hash).to_sym
50
+ end
51
+ end
@@ -0,0 +1,14 @@
1
+ class QuestionReplyType
2
+ def self.to_sym(query)
3
+ case query.value(QV.replyType)
4
+ when QV.Location
5
+ :location
6
+ when QV.Temperature
7
+ :temperature
8
+ when QV.Select
9
+ :select
10
+ else
11
+ nil
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../services/question_references'
2
+
3
+ class Requirement
4
+ attr_reader :query
5
+
6
+ def initialize(node, repository)
7
+ @query = NodeQuery.new(node, repository)
8
+ end
9
+
10
+ def description
11
+ query.value(LV.description)
12
+ end
13
+
14
+ def satisfied?
15
+ query.value(LV.satisfied) == true
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ class Selection
2
+ attr_reader :query
3
+
4
+ include Answer
5
+
6
+ def initialize(node, repository)
7
+ @query = NodeQuery.new(node, repository)
8
+ end
9
+
10
+ def options
11
+ if question.query.value_exists?(QV.options)
12
+ RDF::List.new(question.query.value(QV.options), query.repository)
13
+ elsif question.query.value_exists?(QV.option)
14
+ question.query.values(QV.option)
15
+ else
16
+ nil
17
+ end
18
+ end
19
+
20
+ def selected
21
+ query.value(LV.selected)
22
+ end
23
+
24
+ def selected=(value)
25
+ # in case it is an array, store all the answers
26
+ if value.is_a? Array
27
+ value.each { |v| query.set_value(LV.selected, v, false) }
28
+ else
29
+ query.set_value(LV.selected, value)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ class StateChange
2
+ attr_reader :query
3
+
4
+ def initialize(node, repository)
5
+ @query = NodeQuery.new(node, repository)
6
+ end
7
+
8
+ def apply
9
+ new_repo = query.repository.clone
10
+ new_repo.load_and_process_n3(state_rules)
11
+ new_repo
12
+ end
13
+
14
+ private
15
+
16
+ def state_rules
17
+ File.read(File.dirname(__FILE__) + '/../descriptions/states.n3')
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ class Temperature
2
+ attr_reader :query
3
+
4
+ include Answer
5
+
6
+ def initialize(node, repository)
7
+ @query = NodeQuery.new(node, repository)
8
+ end
9
+
10
+ def in_celsius
11
+ query.value(LV.in_celsius)
12
+ end
13
+
14
+ def in_celsius=(value)
15
+ query.set_value(LV.in_celsius, value)
16
+ end
17
+ end