roadforest 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/file-management.rb +7 -1
- data/lib/roadforest/application.rb +0 -2
- data/lib/roadforest/application/dispatcher.rb +20 -1
- data/lib/roadforest/model.rb +4 -4
- data/lib/roadforest/rdf/{context-fascade.rb → access-manager.rb} +96 -42
- data/lib/roadforest/rdf/focus-list.rb +1 -1
- data/lib/roadforest/rdf/graph-copier.rb +10 -5
- data/lib/roadforest/rdf/graph-focus.rb +222 -32
- data/lib/roadforest/rdf/graph-store.rb +1 -12
- data/lib/roadforest/rdf/investigation.rb +3 -1
- data/lib/roadforest/rdf/post-focus.rb +7 -15
- data/lib/roadforest/remote-host.rb +20 -8
- data/lib/roadforest/resource/rdf/read-only.rb +5 -2
- data/lib/roadforest/test-support/matchers.rb +40 -9
- data/lib/roadforest/utility/class-registry.rb +8 -1
- data/spec/focus-list.rb +7 -1
- data/spec/graph-copier.rb +27 -43
- data/spec/graph-store.rb +41 -15
- data/spec/update-focus.rb +10 -8
- metadata +63 -12
- data/lib/roadforest/rdf/graph-reading.rb +0 -200
- data/lib/roadforest/rdf/update-focus.rb +0 -18
- data/lib/roadforest/resource/handlers.rb +0 -43
data/examples/file-management.rb
CHANGED
@@ -56,7 +56,7 @@ module FileManagementExample
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def add_child(graph)
|
59
|
-
services.logger.debug(graph.source_graph.dump(:nquads))
|
59
|
+
services.logger.debug(graph.access_manager.source_graph.dump(:nquads))
|
60
60
|
new_file = FileRecord.new(graph.first(:lc, "name"), false)
|
61
61
|
services.file_records << new_file
|
62
62
|
end
|
@@ -65,6 +65,12 @@ module FileManagementExample
|
|
65
65
|
graph.add_list(:lc, "needs") do |list|
|
66
66
|
services.file_records.each do |record|
|
67
67
|
if !record.resolved
|
68
|
+
# need = copy_from(:need, '*' => record.name)
|
69
|
+
# list.add_node(need.url) do |node|
|
70
|
+
# need.target = node
|
71
|
+
# need[:lc, :name]
|
72
|
+
# need[:lc, :digest]
|
73
|
+
# end
|
68
74
|
list << path_for(:need, '*' => record.name)
|
69
75
|
end
|
70
76
|
end
|
@@ -3,7 +3,6 @@ module RoadForest
|
|
3
3
|
class Application < Webmachine::Application; end
|
4
4
|
end
|
5
5
|
|
6
|
-
require 'roadforest/resource/handlers'
|
7
6
|
require 'roadforest/application/dispatcher'
|
8
7
|
require 'roadforest/application/path-provider'
|
9
8
|
require 'roadforest/application/services-host'
|
@@ -14,7 +13,6 @@ require 'roadforest/rdf/normalization'
|
|
14
13
|
module RoadForest
|
15
14
|
class Application
|
16
15
|
include RDF::Normalization
|
17
|
-
include Resource::Handlers
|
18
16
|
|
19
17
|
def initialize(canonical_host, services = nil, configuration = nil, dispatcher = nil)
|
20
18
|
@canonical_host = normalize_resource(canonical_host)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'webmachine'
|
2
|
+
require 'roadforest/application/route-adapter'
|
2
3
|
|
3
4
|
module RoadForest
|
4
5
|
class Dispatcher < Webmachine::Dispatcher
|
5
|
-
include Resource::Handlers
|
6
6
|
def initialize(services)
|
7
7
|
super(method(:create_resource))
|
8
8
|
@services = services
|
@@ -11,6 +11,25 @@ module RoadForest
|
|
11
11
|
end
|
12
12
|
attr_accessor :services, :trace_by_default
|
13
13
|
|
14
|
+
def bundle(resource_class, &block)
|
15
|
+
Application::RouteAdapter.new(resource_class, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def bundle_typed_resource(resource_type, model_class, route_name)
|
19
|
+
resource_class = Resource.get(resource_type)
|
20
|
+
bundle(resource_class) do |resource, request, response|
|
21
|
+
resource.model = model_class.new(route_name, resource.params, services)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def bundle_traced_resource(resource_type, model_class, route_name)
|
26
|
+
resource_class = Resource.get(resource_type)
|
27
|
+
bundle(resource_class) do |resource, request, response|
|
28
|
+
resource.model = model_class.new(route_name, resource.params, services)
|
29
|
+
resource.trace = true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
14
33
|
def resource_route(resource, name, path_spec, bindings)
|
15
34
|
route = Route.new(path_spec, resource, bindings || {})
|
16
35
|
yield route if block_given?
|
data/lib/roadforest/model.rb
CHANGED
@@ -133,9 +133,9 @@ module RoadForest
|
|
133
133
|
end
|
134
134
|
|
135
135
|
def start_focus(graph, resource_url=nil)
|
136
|
-
|
137
|
-
|
138
|
-
focus
|
136
|
+
access = RDF::WriteManager.new
|
137
|
+
access.source_graph = graph
|
138
|
+
focus = RDF::GraphFocus.new(access, resource_url || my_url)
|
139
139
|
|
140
140
|
yield focus if block_given?
|
141
141
|
return focus
|
@@ -158,7 +158,7 @@ module RoadForest
|
|
158
158
|
graph = ::RDF::Graph.new
|
159
159
|
focus = start_focus(graph, my_url)
|
160
160
|
fill_graph(focus)
|
161
|
-
self.response_data =
|
161
|
+
self.response_data = graph
|
162
162
|
end
|
163
163
|
end
|
164
164
|
end
|
@@ -2,19 +2,16 @@ require 'rdf'
|
|
2
2
|
require 'roadforest/rdf/resource-query'
|
3
3
|
require 'roadforest/rdf/resource-pattern'
|
4
4
|
require 'roadforest/rdf/normalization'
|
5
|
+
require 'roadforest/rdf/parcel'
|
5
6
|
|
6
7
|
module RoadForest::RDF
|
7
|
-
class
|
8
|
+
class ReadOnlyManager
|
8
9
|
include ::RDF::Countable
|
9
10
|
include ::RDF::Enumerable
|
10
11
|
include ::RDF::Queryable
|
11
12
|
include Normalization
|
12
13
|
|
13
|
-
attr_accessor :resource, :rigor, :source_graph
|
14
|
-
|
15
|
-
def initialize
|
16
|
-
@copied_contexts = {}
|
17
|
-
end
|
14
|
+
attr_accessor :resource, :rigor, :source_graph
|
18
15
|
|
19
16
|
def resource=(resource)
|
20
17
|
@resource = normalize_context(resource)
|
@@ -24,33 +21,14 @@ module RoadForest::RDF
|
|
24
21
|
other = self.class.allocate
|
25
22
|
other.resource = self.resource
|
26
23
|
other.rigor = self.rigor
|
27
|
-
|
28
|
-
other.copied_contexts = self.copied_contexts
|
29
24
|
other.source_graph = self.source_graph
|
30
|
-
|
25
|
+
|
31
26
|
return other
|
32
27
|
end
|
33
28
|
|
34
|
-
|
35
|
-
|
36
|
-
begin
|
37
|
-
parceller = Parcel.new
|
38
|
-
parceller.graph = source_graph
|
39
|
-
parceller
|
40
|
-
end
|
41
|
-
end
|
29
|
+
alias origin_graph source_graph
|
30
|
+
alias destination_graph source_graph
|
42
31
|
|
43
|
-
def copy_context
|
44
|
-
return if copied_contexts[resource]
|
45
|
-
return if target_graph.nil? or source_graph == target_graph
|
46
|
-
parceller.graph_for(resource).each_statement do |statement|
|
47
|
-
statement.context = resource
|
48
|
-
target_graph << statement
|
49
|
-
end
|
50
|
-
copied_contexts[resource] = true
|
51
|
-
end
|
52
|
-
|
53
|
-
#superfluous?
|
54
32
|
def build_query
|
55
33
|
ResourceQuery.new([], {}) do |query|
|
56
34
|
query.subject_context = resource
|
@@ -59,6 +37,10 @@ module RoadForest::RDF
|
|
59
37
|
end
|
60
38
|
end
|
61
39
|
|
40
|
+
def relevant_prefixes
|
41
|
+
relevant_prefixes_for_graph(origin_graph)
|
42
|
+
end
|
43
|
+
|
62
44
|
def query_execute(query, &block)
|
63
45
|
query = ResourceQuery.from(query, resource, rigor)
|
64
46
|
execute_search(query, &block)
|
@@ -69,32 +51,104 @@ module RoadForest::RDF
|
|
69
51
|
execute_search(pattern, &block)
|
70
52
|
end
|
71
53
|
|
72
|
-
def
|
73
|
-
|
74
|
-
enum = search.execute(target_graph)
|
75
|
-
if enum.any?{ true }
|
76
|
-
enum.each(&block)
|
77
|
-
return enum
|
78
|
-
end
|
79
|
-
end
|
80
|
-
search.execute(source_graph, &block)
|
54
|
+
def each(&block)
|
55
|
+
origin_graph.each(&block)
|
81
56
|
end
|
82
57
|
|
83
|
-
def
|
84
|
-
|
58
|
+
def execute_search(search, &block)
|
59
|
+
search.execute(origin_graph, &block)
|
85
60
|
end
|
61
|
+
end
|
86
62
|
|
63
|
+
class WriteManager < ReadOnlyManager
|
87
64
|
def insert(statement)
|
88
|
-
copy_context
|
89
65
|
statement[3] = resource
|
90
|
-
|
66
|
+
destination_graph.insert(statement)
|
91
67
|
end
|
92
68
|
|
93
69
|
def delete(statement)
|
94
70
|
statement = RDF::Query::Pattern.from(statement)
|
95
71
|
statement.context = resource
|
72
|
+
destination_graph.delete(statement)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class PostManager < WriteManager
|
77
|
+
end
|
78
|
+
|
79
|
+
class SplitManager < WriteManager
|
80
|
+
attr_accessor :target_graph
|
81
|
+
|
82
|
+
alias destination_graph target_graph
|
83
|
+
|
84
|
+
def dup
|
85
|
+
other = super
|
86
|
+
other.target_graph = self.target_graph
|
87
|
+
return other
|
88
|
+
end
|
89
|
+
|
90
|
+
def relevant_prefixes
|
91
|
+
super.merge(relevant_prefixes_for_graph(destination_graph))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class UpdateManager < SplitManager
|
96
|
+
def initialize
|
97
|
+
@copied_contexts = {}
|
98
|
+
end
|
99
|
+
|
100
|
+
attr_accessor :copied_contexts
|
101
|
+
|
102
|
+
def dup
|
103
|
+
other = super
|
104
|
+
other.copied_contexts = self.copied_contexts
|
105
|
+
return other
|
106
|
+
end
|
107
|
+
|
108
|
+
def execute_search(search, &block)
|
109
|
+
enum = search.execute(destination_graph)
|
110
|
+
if enum.any?{ true }
|
111
|
+
enum.each(&block)
|
112
|
+
return enum
|
113
|
+
end
|
114
|
+
search.execute(origin_graph, &block)
|
115
|
+
end
|
116
|
+
|
117
|
+
def insert(statement)
|
118
|
+
copy_context
|
119
|
+
super
|
120
|
+
end
|
121
|
+
|
122
|
+
def delete(statement)
|
96
123
|
copy_context
|
97
|
-
|
124
|
+
super
|
125
|
+
end
|
126
|
+
|
127
|
+
def parceller
|
128
|
+
@parceller ||=
|
129
|
+
begin
|
130
|
+
parceller = Parcel.new
|
131
|
+
parceller.graph = source_graph
|
132
|
+
parceller
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def copy_context
|
137
|
+
return if copied_contexts[resource]
|
138
|
+
parceller.graph_for(resource).each_statement do |statement|
|
139
|
+
statement.context = resource
|
140
|
+
destination_graph << statement
|
141
|
+
end
|
142
|
+
copied_contexts[resource] = true
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class CopyManager < SplitManager
|
147
|
+
def execute_search(search, &block)
|
148
|
+
super(search) do |statement|
|
149
|
+
destination_graph.insert(statement)
|
150
|
+
yield statement
|
151
|
+
end
|
98
152
|
end
|
99
153
|
end
|
100
154
|
end
|
@@ -37,7 +37,7 @@ module RoadForest::RDF
|
|
37
37
|
|
38
38
|
if empty?
|
39
39
|
new_subject = subject
|
40
|
-
graph.insert([new_subject, RDF.type, RDF.List])
|
40
|
+
#graph.insert([new_subject, RDF.type, RDF.List])
|
41
41
|
else
|
42
42
|
old_subject, new_subject = last_subject, RDF::Node.new
|
43
43
|
graph.delete([old_subject, RDF.rest, RDF.nil])
|
@@ -3,12 +3,17 @@ require 'roadforest/rdf/graph-focus'
|
|
3
3
|
|
4
4
|
module RoadForest
|
5
5
|
module RDF
|
6
|
-
class GraphCopier <
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
class GraphCopier < GraphFocus
|
7
|
+
def query_value(query)
|
8
|
+
#This isn't the most efficient way to do this (the query essentially
|
9
|
+
#happens twice) but the intended use of GC is to copy small numbers of
|
10
|
+
#patterns between small graphs, so the n is small
|
11
|
+
query.patterns.each do |pattern|
|
12
|
+
pattern.execute(@access_manager) do |statement|
|
13
|
+
@access_manager.insert(statement)
|
14
|
+
end
|
15
|
+
end
|
10
16
|
super
|
11
|
-
@target_graph = ::RDF::Graph.new
|
12
17
|
end
|
13
18
|
end
|
14
19
|
end
|
@@ -1,39 +1,94 @@
|
|
1
1
|
require 'rdf'
|
2
|
-
require '
|
3
|
-
require 'roadforest/rdf
|
2
|
+
require 'rdf/model/node'
|
3
|
+
require 'roadforest/rdf'
|
4
|
+
require 'roadforest/rdf/focus-list'
|
5
|
+
require 'roadforest/rdf/normalization'
|
6
|
+
require 'roadforest/rdf/resource-query'
|
7
|
+
require 'roadforest/rdf/access-manager'
|
8
|
+
|
4
9
|
|
5
10
|
module RoadForest::RDF
|
6
|
-
class
|
7
|
-
|
11
|
+
class NullFocus < ::BasicObject
|
12
|
+
def initialize(focus, pattern, callsite)
|
13
|
+
@focus, @pattern, @callsite = focus, pattern, callsite
|
14
|
+
end
|
15
|
+
|
16
|
+
def __callsite__
|
17
|
+
@callsite
|
18
|
+
end
|
19
|
+
|
20
|
+
def subject
|
21
|
+
@focus.subject
|
22
|
+
end
|
23
|
+
|
24
|
+
def nil?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def blank?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def empty?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
def length
|
37
|
+
0
|
38
|
+
end
|
39
|
+
alias count length
|
40
|
+
alias size length
|
41
|
+
|
42
|
+
def method_missing(method, *args, &block)
|
43
|
+
::Kernel.raise ::NoMethodError, "No method '#{method}' on NullFocus. " +
|
44
|
+
"NullFocus returned because there were no results at \n#{@focus.subject}\n for \n#{@pattern.inspect}\n" +
|
45
|
+
"Search was attempted at #{@callsite[0]}"
|
46
|
+
end
|
8
47
|
end
|
9
48
|
|
10
|
-
class
|
11
|
-
|
12
|
-
|
13
|
-
|
49
|
+
class GraphFocus
|
50
|
+
#XXX Any changes to this class heirarchy or to ContextFascade should start
|
51
|
+
#with a refactor like:
|
52
|
+
# Reduce this to the single-node API ([] []=)
|
53
|
+
# Change the ContextFascade into a family of classes (RO, RW, Update)
|
54
|
+
include Normalization
|
55
|
+
|
56
|
+
#attr_accessor :source_graph, :target_graph, :subject, :root_url,
|
57
|
+
#:source_rigor
|
58
|
+
|
59
|
+
attr_accessor :subject, :access_manager
|
60
|
+
|
61
|
+
alias rdf subject
|
62
|
+
|
63
|
+
def initialize(access_manager, subject = nil)
|
64
|
+
@access_manager = access_manager
|
65
|
+
self.subject = subject unless subject.nil?
|
14
66
|
end
|
15
67
|
|
16
|
-
def
|
17
|
-
|
68
|
+
def root_url
|
69
|
+
@access_manager.resource
|
18
70
|
end
|
19
71
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
else
|
27
|
-
values.each
|
72
|
+
def subject=(*value)
|
73
|
+
@subject = normalize_resource(value)
|
74
|
+
case @subject
|
75
|
+
when ::RDF::URI
|
76
|
+
@access_manager.resource = @subject
|
28
77
|
end
|
29
78
|
end
|
30
79
|
|
31
|
-
def
|
32
|
-
|
80
|
+
def inspect
|
81
|
+
"#<#{self.class.name}:0x#{"%x" % object_id} (#{subject.to_s}) #{forward_properties.inspect}>"
|
82
|
+
end
|
83
|
+
alias to_s inspect
|
84
|
+
|
85
|
+
def dup
|
86
|
+
other = self.class.new(access_manager.dup)
|
87
|
+
other.subject = subject
|
88
|
+
other
|
33
89
|
end
|
34
|
-
end
|
35
90
|
|
36
|
-
|
91
|
+
### Begin old GraphFocus
|
37
92
|
def normalize_triple(property, value, extra=nil)
|
38
93
|
if not extra.nil?
|
39
94
|
property = [property, value]
|
@@ -71,6 +126,7 @@ module RoadForest::RDF
|
|
71
126
|
end
|
72
127
|
value
|
73
128
|
end
|
129
|
+
alias first_or_add find_or_add
|
74
130
|
|
75
131
|
def set_node(property, url=nil)
|
76
132
|
create_node(url) do |node|
|
@@ -100,20 +156,154 @@ module RoadForest::RDF
|
|
100
156
|
yield list if block_given?
|
101
157
|
return list
|
102
158
|
end
|
103
|
-
|
159
|
+
#### End of old GraphFocus
|
160
|
+
|
161
|
+
def wrap_node(value)
|
162
|
+
next_step = dup
|
163
|
+
if ::RDF::Node === value
|
164
|
+
next_step.root_url = self.root_url
|
165
|
+
else
|
166
|
+
next_step.root_url = normalize_context(value)
|
167
|
+
end
|
168
|
+
next_step.subject = value
|
169
|
+
next_step
|
170
|
+
end
|
171
|
+
|
172
|
+
def unwrap_value(value)
|
173
|
+
return nil if value.nil?
|
174
|
+
if value.respond_to? :object
|
175
|
+
value.object
|
176
|
+
else
|
177
|
+
wrap_node(value)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def to_context
|
182
|
+
normalize_context(subject)
|
183
|
+
end
|
184
|
+
|
185
|
+
def root_url=(*value) #XXX curies?
|
186
|
+
@root_url = normalize_resource(value)
|
187
|
+
end
|
104
188
|
|
105
|
-
|
106
|
-
def
|
107
|
-
|
189
|
+
#XXX This probably wants to be handled completely in the MediaType handler
|
190
|
+
def relevant_prefixes
|
191
|
+
access_manager.relevant_prefixes
|
108
192
|
end
|
109
193
|
|
110
|
-
def
|
111
|
-
|
112
|
-
|
194
|
+
def get(prefix, property = nil)
|
195
|
+
return maybe_null( prefix, property,
|
196
|
+
single_or_enum(forward_query_value( prefix, property))
|
197
|
+
)
|
113
198
|
end
|
114
|
-
|
199
|
+
alias_method :[], :get
|
115
200
|
|
116
|
-
|
117
|
-
|
201
|
+
def first(prefix, property = nil)
|
202
|
+
return maybe_null( prefix, property,
|
203
|
+
forward_query_value( prefix, property ).first
|
204
|
+
)
|
205
|
+
end
|
206
|
+
|
207
|
+
def all(prefix, property = nil)
|
208
|
+
return maybe_null( prefix, property,
|
209
|
+
forward_query_value( prefix, property )
|
210
|
+
)
|
211
|
+
end
|
212
|
+
|
213
|
+
#XXX Maybe rev should return a decorator, so it looks like:
|
214
|
+
#focus.rev.get(...) or focus.rev.all(...)
|
215
|
+
def rev(prefix, property = nil)
|
216
|
+
return maybe_null( prefix, property,
|
217
|
+
single_or_enum(reverse_query_value( prefix, property))
|
218
|
+
)
|
219
|
+
end
|
220
|
+
|
221
|
+
def rev_first(prefix, property = nil)
|
222
|
+
return maybe_null( prefix, property,
|
223
|
+
reverse_query_value(prefix, property).first
|
224
|
+
)
|
225
|
+
end
|
226
|
+
|
227
|
+
def rev_all(prefix, property = nil)
|
228
|
+
return maybe_null( prefix, property,
|
229
|
+
reverse_query_value(prefix, property)
|
230
|
+
)
|
231
|
+
end
|
232
|
+
|
233
|
+
def as_list
|
234
|
+
list = FocusList.new(subject, access_manager)
|
235
|
+
list.base_node = self
|
236
|
+
list
|
237
|
+
end
|
238
|
+
|
239
|
+
def forward_properties
|
240
|
+
query_properties( build_query{|q| q.pattern([ normalize_resource(subject), :property, :value ])} )
|
241
|
+
end
|
242
|
+
|
243
|
+
def reverse_properties
|
244
|
+
query_properties( build_query{|q| q.pattern([ :value, :property, normalize_resource(subject)])} )
|
245
|
+
end
|
246
|
+
|
247
|
+
protected
|
248
|
+
|
249
|
+
STRIP_TRACE = %r{\A#{File::expand_path("../../..", __FILE__)}}
|
250
|
+
def maybe_null(prefix, property, result)
|
251
|
+
if not result.nil?
|
252
|
+
if result.respond_to? :empty?
|
253
|
+
return result unless result.empty?
|
254
|
+
else
|
255
|
+
return result
|
256
|
+
end
|
257
|
+
end
|
258
|
+
return NullFocus.new(self, normalize_property(prefix, property), caller(0).drop_while{|line| line =~ STRIP_TRACE})
|
259
|
+
end
|
260
|
+
|
261
|
+
def single_or_enum(values)
|
262
|
+
case values.length
|
263
|
+
when 0
|
264
|
+
return nil
|
265
|
+
when 1
|
266
|
+
return values.first
|
267
|
+
else
|
268
|
+
return values.enum_for(:each)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def reverse_query_value(prefix, property=nil)
|
273
|
+
query_value(build_query{|q|
|
274
|
+
q.pattern([ :value, normalize_property(prefix, property), normalize_resource(subject)])
|
275
|
+
})
|
276
|
+
end
|
277
|
+
|
278
|
+
def forward_query_value(prefix, property=nil)
|
279
|
+
query_value(build_query{|q|
|
280
|
+
q.pattern([ normalize_resource(subject), normalize_property(prefix, property), :value])
|
281
|
+
})
|
282
|
+
end
|
283
|
+
|
284
|
+
def build_query(&block)
|
285
|
+
access_manager.build_query(&block)
|
286
|
+
end
|
287
|
+
|
288
|
+
def execute_query(query)
|
289
|
+
query.execute(access_manager)
|
290
|
+
end
|
291
|
+
|
292
|
+
def query_value(query)
|
293
|
+
solutions = execute_query(query)
|
294
|
+
solutions.map do |solution|
|
295
|
+
unwrap_value(solution.value)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def query_properties(query)
|
300
|
+
Hash[execute_query(query).map do |solution|
|
301
|
+
prop = solution.property
|
302
|
+
if qname = prop.qname
|
303
|
+
prop = qname
|
304
|
+
end
|
305
|
+
[prop, solution.value]
|
306
|
+
end]
|
307
|
+
end
|
118
308
|
end
|
119
309
|
end
|