roadforest 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/file-management.rb +2 -1
- data/lib/roadforest/application/dispatcher.rb +12 -2
- data/lib/roadforest/application/services-host.rb +1 -1
- data/lib/roadforest/application.rb +12 -5
- data/lib/roadforest/blob-model.rb +7 -1
- data/lib/roadforest/content-handling/engine.rb +4 -1
- data/lib/roadforest/content-handling/type-handlers/jsonld.rb +25 -17
- data/lib/roadforest/http/adapters/excon.rb +1 -1
- data/lib/roadforest/http/graph-response.rb +18 -4
- data/lib/roadforest/http/graph-transfer.rb +52 -8
- data/lib/roadforest/http/message.rb +10 -1
- data/lib/roadforest/model.rb +17 -4
- data/lib/roadforest/rdf/context-fascade.rb +80 -5
- data/lib/roadforest/rdf/etagging.rb +53 -0
- data/lib/roadforest/rdf/focus-list.rb +47 -5
- data/lib/roadforest/rdf/graph-copier.rb +4 -5
- data/lib/roadforest/rdf/graph-focus.rb +38 -14
- data/lib/roadforest/rdf/graph-reading.rb +102 -47
- data/lib/roadforest/rdf/graph-store.rb +12 -8
- data/lib/roadforest/rdf/investigation.rb +0 -1
- data/lib/roadforest/rdf/normalization.rb +37 -4
- data/lib/roadforest/rdf/resource-pattern.rb +1 -0
- data/lib/roadforest/rdf/source-rigor/credence-annealer.rb +2 -0
- data/lib/roadforest/rdf/source-rigor/http-investigator.rb +12 -14
- data/lib/roadforest/rdf/update-focus.rb +7 -62
- data/lib/roadforest/remote-host.rb +21 -10
- data/lib/roadforest/resource/rdf/read-only.rb +4 -0
- data/lib/roadforest/server.rb +22 -0
- data/lib/roadforest/test-support/dispatcher-facade.rb +1 -1
- data/lib/roadforest/test-support/http-client.rb +8 -0
- data/spec/client.rb +20 -2
- data/spec/credence-annealer.rb +1 -1
- data/spec/excon-adapater.rb +30 -0
- data/spec/focus-list.rb +34 -0
- data/spec/form-parsing.rb +1 -0
- data/spec/full-integration.rb +193 -0
- data/spec/rdf-normalization.rb +17 -0
- data/spec/update-focus.rb +37 -0
- metadata +22 -17
- data/lib/roadforest/rdf/focus-wrapping.rb +0 -30
data/examples/file-management.rb
CHANGED
@@ -56,6 +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
60
|
new_file = FileRecord.new(graph.first(:lc, "name"), false)
|
60
61
|
services.file_records << new_file
|
61
62
|
end
|
@@ -83,7 +84,7 @@ module FileManagementExample
|
|
83
84
|
end
|
84
85
|
|
85
86
|
def graph_update(graph)
|
86
|
-
data.resolved = graph[
|
87
|
+
data.resolved = graph[:lc, "resolved"]
|
87
88
|
new_graph
|
88
89
|
end
|
89
90
|
|
@@ -7,8 +7,9 @@ module RoadForest
|
|
7
7
|
super(method(:create_resource))
|
8
8
|
@services = services
|
9
9
|
@route_names = {}
|
10
|
+
@trace_by_default = false
|
10
11
|
end
|
11
|
-
attr_accessor :services
|
12
|
+
attr_accessor :services, :trace_by_default
|
12
13
|
|
13
14
|
def resource_route(resource, name, path_spec, bindings)
|
14
15
|
route = Route.new(path_spec, resource, bindings || {})
|
@@ -19,10 +20,19 @@ module RoadForest
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def add_route(name, path_spec, resource_type, model_class, bindings = nil, &block)
|
23
|
+
if trace_by_default
|
24
|
+
return add_traced_route(name, path_spec, resource_type, model_class, bindings, &block)
|
25
|
+
else
|
26
|
+
return add_untraced_route(name, path_spec, resource_type, model_class, bindings, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
alias add add_route
|
30
|
+
|
31
|
+
def add_untraced_route(name, path_spec, resource_type, model_class, bindings = nil, &block)
|
22
32
|
resource = bundle_typed_resource(resource_type, model_class, name)
|
23
33
|
resource_route(resource, name, path_spec, bindings, &block)
|
24
34
|
end
|
25
|
-
alias
|
35
|
+
alias add_untraced add_untraced_route
|
26
36
|
|
27
37
|
def add_traced_route(name, path_spec, resource_type, model_class, bindings = nil, &block)
|
28
38
|
resource = bundle_traced_resource(resource_type, model_class, name)
|
@@ -9,17 +9,19 @@ require 'roadforest/application/path-provider'
|
|
9
9
|
require 'roadforest/application/services-host'
|
10
10
|
require 'roadforest/resource/rdf'
|
11
11
|
require 'roadforest/content-handling/engine'
|
12
|
+
require 'roadforest/rdf/normalization'
|
12
13
|
|
13
14
|
module RoadForest
|
14
15
|
class Application
|
16
|
+
include RDF::Normalization
|
15
17
|
include Resource::Handlers
|
16
18
|
|
17
|
-
def initialize(canonical_host, services, configuration = nil, dispatcher = nil)
|
18
|
-
@canonical_host =
|
19
|
+
def initialize(canonical_host, services = nil, configuration = nil, dispatcher = nil)
|
20
|
+
@canonical_host = normalize_resource(canonical_host)
|
19
21
|
configuration ||= Webmachine::Configuration.default
|
20
22
|
dispatcher ||= Dispatcher.new(services)
|
21
23
|
super(configuration, dispatcher)
|
22
|
-
self.services = services
|
24
|
+
self.services = services unless services.nil?
|
23
25
|
|
24
26
|
setup
|
25
27
|
end
|
@@ -27,16 +29,21 @@ module RoadForest
|
|
27
29
|
def setup
|
28
30
|
end
|
29
31
|
|
30
|
-
|
32
|
+
attr_accessor :services, :canonical_host
|
31
33
|
|
32
34
|
alias router dispatcher
|
33
35
|
|
34
36
|
def services=(service_host)
|
35
37
|
router.services = service_host
|
36
38
|
@services = service_host
|
37
|
-
@services.canonical_host =
|
39
|
+
@services.canonical_host = canonical_host
|
38
40
|
@services.router = PathProvider.new(@dispatcher)
|
39
41
|
@services.type_handling ||= ContentHandling::Engine.default
|
42
|
+
@services.logger ||=
|
43
|
+
begin
|
44
|
+
require 'logger'
|
45
|
+
Logger.new("roadforest.log")
|
46
|
+
end
|
40
47
|
end
|
41
48
|
end
|
42
49
|
end
|
@@ -44,12 +44,18 @@ module RoadForest
|
|
44
44
|
File::open(path)
|
45
45
|
end
|
46
46
|
|
47
|
+
def incomplete_path
|
48
|
+
[path,"incomplete"].join(".")
|
49
|
+
end
|
50
|
+
|
47
51
|
def update(incoming)
|
48
|
-
File::open(
|
52
|
+
File::open(incomplete_path, "w") do |file|
|
49
53
|
incoming.each do |chunk|
|
50
54
|
file.write(chunk)
|
51
55
|
end
|
52
56
|
end
|
57
|
+
Pathname.new(incomplete_path).rename(path)
|
58
|
+
|
53
59
|
return nil
|
54
60
|
end
|
55
61
|
end
|
@@ -2,6 +2,8 @@ require 'roadforest/content-handling/media-type'
|
|
2
2
|
|
3
3
|
module RoadForest
|
4
4
|
module ContentHandling
|
5
|
+
class UnrecognizedType < ::StandardError; end
|
6
|
+
|
5
7
|
class Engine
|
6
8
|
class TypeHandlerList
|
7
9
|
def initialize(prefix)
|
@@ -39,7 +41,7 @@ module RoadForest
|
|
39
41
|
type = MediaType.parse(type)
|
40
42
|
@handlers.fetch(type)
|
41
43
|
rescue KeyError
|
42
|
-
raise "No Content-Type handler for #{
|
44
|
+
raise UnrecognizedType, "No Content-Type handler for #{type}"
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
@@ -105,6 +107,7 @@ module RoadForest
|
|
105
107
|
# Given the 'Accept' header and provided types, chooses an
|
106
108
|
# appropriate media type.
|
107
109
|
def choose_media_type(provided, header)
|
110
|
+
return "*/*" if header.nil?
|
108
111
|
requested = MediaTypeList.build(header.split(/\s*,\s*/))
|
109
112
|
requested.best_match_from(provided)
|
110
113
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
#@require 'rdf/rdfa' #XXX Otherwise json-ld grabs RDFa documents. Awaiting fix
|
2
2
|
#upstream
|
3
3
|
require 'json/ld'
|
4
|
+
require 'roadforest/rdf/normalization'
|
4
5
|
|
5
6
|
module RoadForest
|
6
7
|
module MediaType
|
@@ -13,13 +14,13 @@ module RoadForest
|
|
13
14
|
end
|
14
15
|
attr_reader :type, :handler
|
15
16
|
|
16
|
-
def local_to_network(network)
|
17
|
-
@handler.local_to_network(network)
|
17
|
+
def local_to_network(base_uri, network)
|
18
|
+
@handler.local_to_network(base_uri, network)
|
18
19
|
end
|
19
20
|
alias from_graph local_to_network
|
20
21
|
|
21
|
-
def network_to_local(source)
|
22
|
-
@handler.network_to_local(source)
|
22
|
+
def network_to_local(base_uri, source)
|
23
|
+
@handler.network_to_local(base_uri, source)
|
23
24
|
end
|
24
25
|
alias to_graph network_to_local
|
25
26
|
end
|
@@ -46,23 +47,23 @@ module RoadForest
|
|
46
47
|
end
|
47
48
|
|
48
49
|
class Handler
|
49
|
-
def network_to_local(network)
|
50
|
+
def network_to_local(base_uri, network)
|
50
51
|
return network
|
51
52
|
end
|
52
53
|
|
53
|
-
def local_to_network(local)
|
54
|
+
def local_to_network(base_uri, local)
|
54
55
|
return local
|
55
56
|
end
|
56
57
|
|
57
58
|
def parse_for(resource)
|
58
59
|
source = resource.request_body
|
59
|
-
input_data = network_to_local(source)
|
60
60
|
model = resource.model
|
61
|
+
input_data = network_to_local(model.my_url, source)
|
61
62
|
|
62
63
|
update_model(model, input_data)
|
63
64
|
|
64
65
|
renderer = model.type_handling.choose_renderer(resource.request_accept_header)
|
65
|
-
body = renderer.local_to_network(model.response_data)
|
66
|
+
body = renderer.local_to_network(model.my_url, model.response_data)
|
66
67
|
|
67
68
|
build_response(resource)
|
68
69
|
end
|
@@ -70,13 +71,13 @@ module RoadForest
|
|
70
71
|
def render_for(resource)
|
71
72
|
model = resource.model
|
72
73
|
output_data = get_output(model)
|
73
|
-
local_to_network(output_data)
|
74
|
+
local_to_network(model.my_url, output_data)
|
74
75
|
end
|
75
76
|
|
76
77
|
def add_child_to(resource)
|
77
78
|
model = resource.model
|
78
79
|
source = resource.request_body
|
79
|
-
input_data = network_to_local(source)
|
80
|
+
input_data = network_to_local(model.my_url, source)
|
80
81
|
|
81
82
|
child_for_model(resource.model, input_data)
|
82
83
|
|
@@ -87,7 +88,7 @@ module RoadForest
|
|
87
88
|
model = resource.model
|
88
89
|
|
89
90
|
renderer = model.type_handling.choose_renderer(resource.request_accept_header)
|
90
|
-
body = renderer.local_to_network(model.response_data)
|
91
|
+
body = renderer.local_to_network(model.my_url, model.response_data)
|
91
92
|
|
92
93
|
resource.response_content_type = renderer.content_type_header
|
93
94
|
resource.response_body = body
|
@@ -115,8 +116,6 @@ module RoadForest
|
|
115
116
|
result
|
116
117
|
end
|
117
118
|
end
|
118
|
-
|
119
|
-
require 'roadforest/rdf/normalization'
|
120
119
|
class RDFHandler < Handler
|
121
120
|
include RDF::Normalization
|
122
121
|
|
@@ -150,17 +149,26 @@ require 'roadforest/rdf/normalization'
|
|
150
149
|
|
151
150
|
#application/ld+json
|
152
151
|
class JSONLD < RDFHandler
|
153
|
-
|
154
|
-
|
152
|
+
include RDF::Normalization
|
153
|
+
|
154
|
+
def local_to_network(base_uri, rdf)
|
155
|
+
raise "Invalid base uri: #{base_uri}" if base_uri.nil?
|
156
|
+
prefixes = relevant_prefixes_for_graph(rdf)
|
157
|
+
prefixes.keys.each do |prefix|
|
158
|
+
prefixes[prefix.to_sym] = prefixes[prefix]
|
159
|
+
end
|
160
|
+
JSON::LD::Writer.buffer(:base_uri => base_uri.to_s,
|
161
|
+
:prefixes => prefixes) do |writer|
|
155
162
|
rdf.each_statement do |statement|
|
156
163
|
writer << statement
|
157
164
|
end
|
158
165
|
end
|
159
166
|
end
|
160
167
|
|
161
|
-
def network_to_local(source)
|
168
|
+
def network_to_local(base_uri, source)
|
169
|
+
raise "Invalid base uri: #{base_uri.inspect}" if base_uri.nil?
|
162
170
|
graph = ::RDF::Graph.new
|
163
|
-
reader = JSON::LD::Reader.new(source.to_s)
|
171
|
+
reader = JSON::LD::Reader.new(source.to_s, :base_uri => base_uri.to_s)
|
164
172
|
reader.each_statement do |statement|
|
165
173
|
graph.insert(statement)
|
166
174
|
end
|
@@ -1,20 +1,34 @@
|
|
1
1
|
module RoadForest
|
2
2
|
module HTTP
|
3
|
-
class
|
4
|
-
attr_accessor :graph
|
3
|
+
class BaseResponse
|
5
4
|
attr_reader :request, :response
|
6
5
|
|
7
|
-
def initialize(request, response
|
8
|
-
@request, @response
|
6
|
+
def initialize(request, response)
|
7
|
+
@request, @response = request, response
|
9
8
|
end
|
10
9
|
|
11
10
|
def url
|
12
11
|
request.url
|
13
12
|
end
|
14
13
|
|
14
|
+
def etag
|
15
|
+
response.etag
|
16
|
+
end
|
17
|
+
|
15
18
|
def status
|
16
19
|
response.status
|
17
20
|
end
|
21
|
+
|
22
|
+
def raw_body
|
23
|
+
response.body
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class UnparseableResponse < BaseResponse
|
28
|
+
end
|
29
|
+
|
30
|
+
class GraphResponse < BaseResponse
|
31
|
+
attr_accessor :graph
|
18
32
|
end
|
19
33
|
end
|
20
34
|
end
|
@@ -5,11 +5,18 @@ require 'roadforest/content-handling/engine'
|
|
5
5
|
module RoadForest
|
6
6
|
module HTTP
|
7
7
|
class GraphTransfer
|
8
|
-
|
8
|
+
class Retryable < StandardError; end
|
9
|
+
|
10
|
+
attr_accessor :http_client, :trace
|
9
11
|
attr_writer :type_handling
|
12
|
+
attr_reader :graph_cache
|
10
13
|
|
11
14
|
def initialize
|
15
|
+
@trace = nil
|
12
16
|
@type_preferences = Hash.new{|h,k| k.nil? ? "*/*" : h[nil]}
|
17
|
+
@graph_cache = Hash.new do |cache, url|
|
18
|
+
cache[url] = {}
|
19
|
+
end
|
13
20
|
end
|
14
21
|
|
15
22
|
def type_handling
|
@@ -37,7 +44,20 @@ module RoadForest
|
|
37
44
|
|
38
45
|
response = send_request(request, graph)
|
39
46
|
|
40
|
-
|
47
|
+
case response
|
48
|
+
when HTTP::Response
|
49
|
+
response = build_response(request, response)
|
50
|
+
cache_response(response)
|
51
|
+
return response
|
52
|
+
when GraphResponse
|
53
|
+
return response
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def cache_response(response)
|
58
|
+
return if response.etag.nil?
|
59
|
+
return if response.etag.empty?
|
60
|
+
graph_cache[response.url][response.etag] = response
|
41
61
|
end
|
42
62
|
|
43
63
|
def validate(method, url, graph)
|
@@ -59,9 +79,18 @@ module RoadForest
|
|
59
79
|
def setup_request(method, url)
|
60
80
|
request = Request.new(method, url)
|
61
81
|
request.headers["Accept"] = type_handling.parsers.types.accept_header
|
82
|
+
add_cache_headers(request)
|
62
83
|
request
|
63
84
|
end
|
64
85
|
|
86
|
+
def add_cache_headers(request)
|
87
|
+
return unless request.method == "GET"
|
88
|
+
return unless graph_cache.has_key?(request.url)
|
89
|
+
cached = graph_cache[request.url]
|
90
|
+
return if cached.empty?
|
91
|
+
request.headers["If-None-Match"] = cached.keys.join(", ")
|
92
|
+
end
|
93
|
+
|
65
94
|
def select_renderer(url)
|
66
95
|
end
|
67
96
|
|
@@ -77,17 +106,28 @@ module RoadForest
|
|
77
106
|
content_type = best_type_for(request.url)
|
78
107
|
renderer = type_handling.choose_renderer(content_type)
|
79
108
|
request.headers["Content-Type"] = renderer.content_type_header
|
80
|
-
request.body_string = renderer.from_graph(graph)
|
109
|
+
request.body_string = renderer.from_graph(request.url, graph)
|
81
110
|
end
|
82
111
|
|
83
|
-
|
112
|
+
def trace_message(message)
|
113
|
+
return unless @trace
|
114
|
+
@trace = $stdout unless IO === @trace
|
115
|
+
@trace.puts message.inspect
|
116
|
+
end
|
84
117
|
|
85
118
|
def send_request(request, graph)
|
86
119
|
retry_limit ||= 5
|
120
|
+
#Check expires headers on received
|
87
121
|
render_graph(graph, request)
|
88
122
|
|
123
|
+
trace_message(request)
|
89
124
|
response = http_client.do_request(request)
|
125
|
+
trace_message(response)
|
90
126
|
case response.status
|
127
|
+
when 304 #Not Modified
|
128
|
+
response = graph_cache.fetch(request.url).fetch(response.etag)
|
129
|
+
trace_message(response)
|
130
|
+
return response
|
91
131
|
when 415 #Type not accepted
|
92
132
|
record_accept_header(request.url, response.headers["Accept"])
|
93
133
|
raise Retryable
|
@@ -98,14 +138,18 @@ module RoadForest
|
|
98
138
|
retry
|
99
139
|
end
|
100
140
|
|
101
|
-
def parse_response(response)
|
141
|
+
def parse_response(base_uri, response)
|
102
142
|
parser = type_handling.choose_parser(response.headers["Content-Type"])
|
103
|
-
parser.to_graph(response.body_string)
|
143
|
+
parser.to_graph(base_uri, response.body_string)
|
104
144
|
end
|
105
145
|
|
106
146
|
def build_response(request, response)
|
107
|
-
graph = parse_response(response)
|
108
|
-
|
147
|
+
graph = parse_response(request.url, response)
|
148
|
+
response = GraphResponse.new(request, response)
|
149
|
+
response.graph = graph
|
150
|
+
return response
|
151
|
+
rescue ContentHandling::UnrecognizedType
|
152
|
+
return UnparseableResponse.new(request, response)
|
109
153
|
end
|
110
154
|
end
|
111
155
|
end
|
@@ -69,6 +69,11 @@ module RoadForest
|
|
69
69
|
def initialize(method, url)
|
70
70
|
super()
|
71
71
|
@method, @url = method, url
|
72
|
+
headers["Host"] = Addressable::URI.parse(url).host
|
73
|
+
end
|
74
|
+
|
75
|
+
def inspect
|
76
|
+
"\n" + super
|
72
77
|
end
|
73
78
|
|
74
79
|
def needs_body?
|
@@ -76,13 +81,17 @@ module RoadForest
|
|
76
81
|
end
|
77
82
|
|
78
83
|
def inspection_payload
|
79
|
-
["#{method} #{url}"] + super
|
84
|
+
[url, "#{method} #{url.path}"] + super
|
80
85
|
end
|
81
86
|
end
|
82
87
|
|
83
88
|
class Response < Message
|
84
89
|
attr_accessor :status
|
85
90
|
|
91
|
+
def etag
|
92
|
+
headers["ETag"]
|
93
|
+
end
|
94
|
+
|
86
95
|
def inspection_payload
|
87
96
|
[status] + super
|
88
97
|
end
|
data/lib/roadforest/model.rb
CHANGED
@@ -12,6 +12,7 @@ module RoadForest
|
|
12
12
|
@response_values = {}
|
13
13
|
end
|
14
14
|
attr_reader :route_name, :params, :services, :data
|
15
|
+
attr_reader :response_values
|
15
16
|
|
16
17
|
def path_for(route_name = nil, params = nil)
|
17
18
|
services.router.path_for(route_name, (params || self.params).to_hash)
|
@@ -103,7 +104,10 @@ module RoadForest
|
|
103
104
|
|
104
105
|
end
|
105
106
|
|
107
|
+
require 'roadforest/rdf/etagging'
|
106
108
|
class RDFModel < Model
|
109
|
+
include RDF::Etagging
|
110
|
+
|
107
111
|
def update(graph)
|
108
112
|
graph_update(start_focus(graph))
|
109
113
|
end
|
@@ -137,13 +141,22 @@ module RoadForest
|
|
137
141
|
return focus
|
138
142
|
end
|
139
143
|
|
140
|
-
def
|
141
|
-
|
142
|
-
|
144
|
+
def etag
|
145
|
+
@etag ||= etag_from(etag_graph)
|
146
|
+
end
|
147
|
+
|
148
|
+
def etag_graph
|
149
|
+
current_graph
|
150
|
+
end
|
151
|
+
|
152
|
+
def current_graph
|
153
|
+
return response_data if response_values.has_key?(:data)
|
154
|
+
new_graph
|
143
155
|
end
|
144
156
|
|
145
157
|
def new_graph
|
146
|
-
|
158
|
+
graph = ::RDF::Graph.new
|
159
|
+
focus = start_focus(graph, my_url)
|
147
160
|
fill_graph(focus)
|
148
161
|
self.response_data = focus.graph
|
149
162
|
end
|
@@ -1,25 +1,100 @@
|
|
1
1
|
require 'rdf'
|
2
|
+
require 'roadforest/rdf/resource-query'
|
3
|
+
require 'roadforest/rdf/resource-pattern'
|
4
|
+
require 'roadforest/rdf/normalization'
|
2
5
|
|
3
6
|
module RoadForest::RDF
|
4
7
|
class ContextFascade
|
5
8
|
include ::RDF::Countable
|
6
9
|
include ::RDF::Enumerable
|
7
10
|
include ::RDF::Queryable
|
11
|
+
include Normalization
|
8
12
|
|
9
|
-
|
10
|
-
|
13
|
+
attr_accessor :resource, :rigor, :source_graph, :target_graph, :copied_contexts
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@copied_contexts = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def resource=(resource)
|
20
|
+
@resource = normalize_context(resource)
|
21
|
+
end
|
22
|
+
|
23
|
+
def dup
|
24
|
+
other = self.class.allocate
|
25
|
+
other.resource = self.resource
|
26
|
+
other.rigor = self.rigor
|
27
|
+
|
28
|
+
other.copied_contexts = self.copied_contexts
|
29
|
+
other.source_graph = self.source_graph
|
30
|
+
other.target_graph = self.target_graph
|
31
|
+
return other
|
32
|
+
end
|
33
|
+
|
34
|
+
def parceller
|
35
|
+
@parceller ||=
|
36
|
+
begin
|
37
|
+
parceller = Parcel.new
|
38
|
+
parceller.graph = source_graph
|
39
|
+
parceller
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
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
|
+
def build_query
|
55
|
+
ResourceQuery.new([], {}) do |query|
|
56
|
+
query.subject_context = resource
|
57
|
+
query.source_rigor = rigor
|
58
|
+
yield query
|
59
|
+
end
|
11
60
|
end
|
12
61
|
|
13
62
|
def query_execute(query, &block)
|
14
|
-
ResourceQuery.from(query,
|
63
|
+
query = ResourceQuery.from(query, resource, rigor)
|
64
|
+
execute_search(query, &block)
|
15
65
|
end
|
16
66
|
|
17
67
|
def query_pattern(pattern, &block)
|
18
|
-
ResourcePattern.from(pattern, {:context_roles => {:subject =>
|
68
|
+
pattern = ResourcePattern.from(pattern, {:context_roles => {:subject => resource}, :source_rigor => rigor})
|
69
|
+
execute_search(pattern, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def execute_search(search, &block)
|
73
|
+
if target_graph != source_graph and not target_graph.nil?
|
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)
|
19
81
|
end
|
20
82
|
|
21
83
|
def each(&block)
|
22
|
-
|
84
|
+
source_graph.each(&block)
|
85
|
+
end
|
86
|
+
|
87
|
+
def insert(statement)
|
88
|
+
copy_context
|
89
|
+
statement[3] = resource
|
90
|
+
target_graph.insert(statement)
|
91
|
+
end
|
92
|
+
|
93
|
+
def delete(statement)
|
94
|
+
statement = RDF::Query::Pattern.from(statement)
|
95
|
+
statement.context = resource
|
96
|
+
copy_context
|
97
|
+
target_graph.delete(statement)
|
23
98
|
end
|
24
99
|
end
|
25
100
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'roadforest/rdf'
|
2
|
+
|
3
|
+
module RoadForest::RDF
|
4
|
+
module Etagging
|
5
|
+
def etag_from(graph)
|
6
|
+
require 'openssl'
|
7
|
+
quads = sorted_quads(graph)
|
8
|
+
mapped = blank_mapped(quads)
|
9
|
+
strings = mapped.map(&:inspect)
|
10
|
+
|
11
|
+
ripe = OpenSSL::Digest::RIPEMD160.new
|
12
|
+
mapped.each do |quad|
|
13
|
+
ripe << quad.inspect
|
14
|
+
end
|
15
|
+
"W/\"#{ripe.base64digest}\""
|
16
|
+
end
|
17
|
+
|
18
|
+
def blank_mapped(quads)
|
19
|
+
sequence = 0
|
20
|
+
mapping = Hash.new do |h,k|
|
21
|
+
h[k] = RDF::Node.new(sequence+=1)
|
22
|
+
end
|
23
|
+
|
24
|
+
quads.map do |quad|
|
25
|
+
quad.map do |term|
|
26
|
+
case term
|
27
|
+
when RDF::Node
|
28
|
+
mapping[term].to_s
|
29
|
+
when nil
|
30
|
+
nil
|
31
|
+
else
|
32
|
+
term.to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def sorted_quads(graph)
|
39
|
+
graph.statements.map do |statement|
|
40
|
+
[statement.subject, statement.predicate, statement.object, statement.context]
|
41
|
+
end.sort_by do |quad|
|
42
|
+
quad.map do |term|
|
43
|
+
case term
|
44
|
+
when RDF::Node
|
45
|
+
nil
|
46
|
+
else
|
47
|
+
term
|
48
|
+
end
|
49
|
+
end.join("/")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|