case_model 0.0.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.
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