roadforest 0.0.1 → 0.0.2
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.
- 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
|