roadforest 0.0.3 → 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.
- checksums.yaml +7 -0
- data/examples/file-management.rb +7 -9
- data/lib/roadforest/application/dispatcher.rb +44 -31
- data/lib/roadforest/application/parameters.rb +5 -4
- data/lib/roadforest/application/path-provider.rb +14 -1
- data/lib/roadforest/application/route-adapter.rb +15 -4
- data/lib/roadforest/application/services-host.rb +41 -7
- data/lib/roadforest/application.rb +4 -8
- data/lib/roadforest/authorization.rb +231 -0
- data/lib/roadforest/blob-model.rb +2 -11
- data/lib/roadforest/content-handling/engine.rb +12 -6
- data/lib/roadforest/content-handling/handler-wrap.rb +45 -0
- data/lib/roadforest/content-handling/media-type.rb +20 -12
- data/lib/roadforest/content-handling/type-handler.rb +76 -0
- data/lib/roadforest/content-handling/type-handlers/jsonld.rb +2 -146
- data/lib/roadforest/content-handling/type-handlers/rdf-handler.rb +38 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/document-environment.rb +34 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/object-environment.rb +62 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/property-environment.rb +46 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/render-engine.rb +574 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/render-environment.rb +144 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/subject-environment.rb +80 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer.rb +163 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa.rb +175 -0
- data/lib/roadforest/content-handling/type-handlers/rdfpost.rb +297 -0
- data/lib/roadforest/debug.rb +10 -0
- data/lib/roadforest/http/graph-response.rb +3 -7
- data/lib/roadforest/http/graph-transfer.rb +28 -106
- data/lib/roadforest/http/keychain.rb +79 -0
- data/lib/roadforest/http/message.rb +9 -1
- data/lib/roadforest/http/user-agent.rb +115 -0
- data/lib/roadforest/http.rb +8 -0
- data/lib/roadforest/model.rb +48 -3
- data/lib/roadforest/rdf/graph-focus.rb +5 -3
- data/lib/roadforest/rdf/graph-store.rb +4 -2
- data/lib/roadforest/rdf/normalization.rb +6 -1
- data/lib/roadforest/remote-host.rb +22 -7
- data/lib/roadforest/resource/rdf/read-only.rb +15 -1
- data/lib/roadforest/templates/base/doc.haml +13 -0
- data/lib/roadforest/templates/base/property_value.haml +12 -0
- data/lib/roadforest/templates/base/property_values.haml +6 -0
- data/lib/roadforest/templates/base/subject.haml +4 -0
- data/lib/roadforest/templates/distiller/doc.haml +20 -0
- data/lib/roadforest/templates/distiller/nil-object.haml +1 -0
- data/lib/roadforest/templates/distiller/property_value.haml +7 -0
- data/lib/roadforest/templates/distiller/property_values.haml +7 -0
- data/lib/roadforest/templates/distiller/subject.haml +5 -0
- data/lib/roadforest/templates/min/doc.haml +10 -0
- data/lib/roadforest/templates/min/property_values.haml +7 -0
- data/lib/roadforest/templates/min/subject.haml +2 -0
- data/lib/roadforest/templates/nil-object.haml +0 -0
- data/lib/roadforest/templates/node-object.haml +1 -0
- data/lib/roadforest/templates/object.haml +1 -0
- data/lib/roadforest/templates/uri-object.haml +1 -0
- data/lib/roadforest/templates/xml-literal-object.haml +1 -0
- data/lib/roadforest/utility/class-registry.rb +4 -0
- data/spec/client.rb +119 -77
- data/spec/{excon-adapater.rb → excon-adapter.rb} +4 -0
- data/spec/full-integration.rb +6 -2
- data/spec/graph-store.rb +1 -1
- data/spec/media-types.rb +29 -2
- metadata +102 -125
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'roadforest/http'
|
2
|
+
require 'roadforest/http/keychain'
|
3
|
+
|
4
|
+
module RoadForest
|
5
|
+
module HTTP
|
6
|
+
class UserAgent
|
7
|
+
def initialize(http_client)
|
8
|
+
@http_client = http_client
|
9
|
+
@trace = nil
|
10
|
+
@cache = Hash.new do |cache, url|
|
11
|
+
cache[url] = {}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :http_client, :trace
|
16
|
+
attr_reader :cache
|
17
|
+
|
18
|
+
def make_request(method, url, headers = nil, body=nil, retry_limit=5)
|
19
|
+
headers ||= {}
|
20
|
+
|
21
|
+
method = method.to_s.upcase
|
22
|
+
|
23
|
+
validate(method, url, headers, body)
|
24
|
+
|
25
|
+
request = setup_request(method, url, headers, body)
|
26
|
+
|
27
|
+
response = send_request(request, retry_limit)
|
28
|
+
|
29
|
+
cache_response(url, response)
|
30
|
+
|
31
|
+
return response
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup_request(method, url, headers, body)
|
35
|
+
request = Request.new(method, url)
|
36
|
+
request.headers.merge!(headers)
|
37
|
+
request.body = body
|
38
|
+
add_authorization(request)
|
39
|
+
add_cache_headers(request)
|
40
|
+
request
|
41
|
+
end
|
42
|
+
|
43
|
+
def send_request(request, retry_limit=5)
|
44
|
+
#Check expires headers on received
|
45
|
+
|
46
|
+
trace_message(request)
|
47
|
+
response = http_client.do_request(request)
|
48
|
+
trace_message(response)
|
49
|
+
case response.status
|
50
|
+
when 304 #Not Modified
|
51
|
+
response = cache.fetch(request.url).fetch(response.etag)
|
52
|
+
trace_message(response)
|
53
|
+
return response
|
54
|
+
when 401
|
55
|
+
#XXX What if challenge matches existing Auth header? i.e. current
|
56
|
+
#creds are wrong?
|
57
|
+
request.headers["Authorization"] = keychain.challenge_response(url, response.headers["WWW-Authenticate"])
|
58
|
+
raise Retryable
|
59
|
+
end
|
60
|
+
return response
|
61
|
+
rescue Retryable
|
62
|
+
raise unless (retry_limit -= 1) > 0
|
63
|
+
retry
|
64
|
+
end
|
65
|
+
|
66
|
+
def trace_message(message)
|
67
|
+
return unless @trace
|
68
|
+
@trace = $stderr unless @trace.respond_to?(:puts)
|
69
|
+
@trace.puts message.inspect
|
70
|
+
end
|
71
|
+
|
72
|
+
def keychain
|
73
|
+
@keychain ||= Keychain.new
|
74
|
+
end
|
75
|
+
|
76
|
+
def cache_response(url, response)
|
77
|
+
return if response.etag.nil?
|
78
|
+
return if response.etag.empty?
|
79
|
+
#XXX large bodies
|
80
|
+
cache[url][response.etag] = response
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate(method, url, headers, body)
|
84
|
+
case method
|
85
|
+
when "GET", "HEAD", "DELETE"
|
86
|
+
raise "Method #{method} requires an empty body" unless body.nil?
|
87
|
+
when "POST", "PATCH", "PUT"
|
88
|
+
raise "Method #{method} requires a body" if body.nil?
|
89
|
+
#when "OPTION", "TRACE" #Need to put verbs where they go
|
90
|
+
else
|
91
|
+
raise "Unrecognized method: #{method}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def add_authorization(request)
|
96
|
+
request.headers["Authorization"] = keychain.preemptive_response(request.url)
|
97
|
+
end
|
98
|
+
|
99
|
+
def add_cache_headers(request)
|
100
|
+
case request.method
|
101
|
+
when "GET"
|
102
|
+
return unless cache.has_key?(request.url)
|
103
|
+
cached = cache[request.url]
|
104
|
+
return if cached.empty?
|
105
|
+
request.headers["If-None-Match"] = cached.keys.join(", ")
|
106
|
+
when "POST", "PUT"
|
107
|
+
return unless cache.has_key?(request.url)
|
108
|
+
cached = cache[request.url]
|
109
|
+
return if cached.empty?
|
110
|
+
request.headers["If-Match"] = cached.keys.join(", ")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/roadforest/model.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
require 'roadforest/rdf/graph-store'
|
2
|
+
require 'roadforest/rdf/etagging'
|
3
|
+
require 'roadforest/rdf/access-manager'
|
4
|
+
require 'roadforest/rdf/graph-focus'
|
5
|
+
|
2
6
|
module RoadForest
|
3
7
|
class ProcessingSequenceError < StandardError
|
4
8
|
end
|
@@ -15,7 +19,34 @@ module RoadForest
|
|
15
19
|
attr_reader :response_values
|
16
20
|
|
17
21
|
def path_for(route_name = nil, params = nil)
|
18
|
-
services.router.path_for(route_name,
|
22
|
+
services.router.path_for(route_name, params || self.params)
|
23
|
+
end
|
24
|
+
|
25
|
+
def url_for(route_name, params = nil)
|
26
|
+
Addressable::URI.parse(canonical_host.to_s).join(path_for(route_name, params))
|
27
|
+
end
|
28
|
+
|
29
|
+
def model_for(route_name = nil, params = nil)
|
30
|
+
services.router.model_for(route_name, params || self.params)
|
31
|
+
end
|
32
|
+
|
33
|
+
def required_grants(method)
|
34
|
+
services.authz.build_grants do |grants|
|
35
|
+
grants.add(:admin)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def authorization(method, header)
|
40
|
+
required = required_grants(method)
|
41
|
+
if required.empty?
|
42
|
+
:public
|
43
|
+
else
|
44
|
+
services.authz.authorization(header, required_grants(method))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def authentication_challenge
|
49
|
+
services.authz.challenge(:realm => "Roadforest")
|
19
50
|
end
|
20
51
|
|
21
52
|
def canonical_host
|
@@ -23,7 +54,7 @@ module RoadForest
|
|
23
54
|
end
|
24
55
|
|
25
56
|
def canonical_uri
|
26
|
-
|
57
|
+
url_for(route_name, params)
|
27
58
|
end
|
28
59
|
|
29
60
|
def type_handling
|
@@ -104,7 +135,6 @@ module RoadForest
|
|
104
135
|
|
105
136
|
end
|
106
137
|
|
107
|
-
require 'roadforest/rdf/etagging'
|
108
138
|
class RDFModel < Model
|
109
139
|
include RDF::Etagging
|
110
140
|
|
@@ -141,6 +171,21 @@ module RoadForest
|
|
141
171
|
return focus
|
142
172
|
end
|
143
173
|
|
174
|
+
def copy_model(node, route_name, params=nil)
|
175
|
+
params ||= {}
|
176
|
+
|
177
|
+
url = url_for(route_name, params)
|
178
|
+
source_model = model_for(route_name, params)
|
179
|
+
|
180
|
+
access = RDF::CopyManager.new
|
181
|
+
access.source_graph = source_model.current_graph
|
182
|
+
access.target_graph = node.access_manager.destination_graph
|
183
|
+
copier = RDF::GraphFocus.new(access, url)
|
184
|
+
|
185
|
+
yield copier if block_given?
|
186
|
+
copier
|
187
|
+
end
|
188
|
+
|
144
189
|
def etag
|
145
190
|
@etag ||= etag_from(etag_graph)
|
146
191
|
end
|
@@ -78,7 +78,7 @@ module RoadForest::RDF
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def inspect
|
81
|
-
"#<#{self.class.name}:0x#{"%x" % object_id} (#{subject.to_s})
|
81
|
+
"#<#{self.class.name}:0x#{"%x" % object_id} s:(#{subject.to_s}) p->o:#{forward_properties.inspect}>" #ok
|
82
82
|
end
|
83
83
|
alias to_s inspect
|
84
84
|
|
@@ -151,7 +151,7 @@ module RoadForest::RDF
|
|
151
151
|
end
|
152
152
|
|
153
153
|
def add_list(property, extra=nil)
|
154
|
-
list = FocusList.new(
|
154
|
+
list = FocusList.new(::RDF::Node.new, access_manager)
|
155
155
|
access_manager.insert([subject, normalize_property(property, extra), list.subject])
|
156
156
|
yield list if block_given?
|
157
157
|
return list
|
@@ -171,8 +171,10 @@ module RoadForest::RDF
|
|
171
171
|
|
172
172
|
def unwrap_value(value)
|
173
173
|
return nil if value.nil?
|
174
|
-
if value
|
174
|
+
if RDF::Literal === value
|
175
175
|
value.object
|
176
|
+
elsif value == RDF.nil
|
177
|
+
nil
|
176
178
|
else
|
177
179
|
wrap_node(value)
|
178
180
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rdf'
|
2
|
+
require 'roadforest/debug'
|
2
3
|
require 'roadforest/rdf/vocabulary'
|
3
4
|
require 'roadforest/rdf/normalization'
|
4
5
|
|
@@ -51,8 +52,9 @@ module RoadForest::RDF
|
|
51
52
|
end
|
52
53
|
|
53
54
|
def debug(message)
|
54
|
-
|
55
|
-
|
55
|
+
io = @debug_io || RoadForest.debug_io
|
56
|
+
return if io.nil?
|
57
|
+
io.puts(message)
|
56
58
|
end
|
57
59
|
|
58
60
|
def repository_dump(format = :turtle)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'addressable/uri'
|
1
2
|
require 'rdf'
|
2
3
|
|
3
4
|
module RoadForest::RDF
|
@@ -118,7 +119,11 @@ module RoadForest::RDF
|
|
118
119
|
end
|
119
120
|
vocab
|
120
121
|
end
|
121
|
-
|
122
|
+
begin
|
123
|
+
vocab[property]
|
124
|
+
rescue KeyError
|
125
|
+
raise KeyError, "No property #{property} in #{vocab.inspect} - (try: #{vocab.methods(false).sort.inspect})"
|
126
|
+
end
|
122
127
|
end
|
123
128
|
|
124
129
|
def expand_curie(from)
|
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'roadforest/rdf/source-rigor'
|
2
2
|
require 'roadforest/rdf/source-rigor/credence-annealer'
|
3
3
|
require 'roadforest/rdf/graph-store'
|
4
|
+
require 'roadforest/http/user-agent'
|
4
5
|
require 'roadforest/http/graph-transfer'
|
5
6
|
require 'roadforest/http/adapters/excon'
|
7
|
+
require 'roadforest/rdf/access-manager'
|
8
|
+
require 'roadforest/rdf/graph-focus'
|
6
9
|
|
7
10
|
module RoadForest
|
8
11
|
class RemoteHost
|
@@ -26,10 +29,20 @@ module RoadForest
|
|
26
29
|
@http_client ||= HTTP::ExconAdapter.new(url)
|
27
30
|
end
|
28
31
|
|
32
|
+
def trace=(target)
|
33
|
+
user_agent.trace = target
|
34
|
+
end
|
35
|
+
|
36
|
+
def user_agent
|
37
|
+
@user_agent ||= HTTP::UserAgent.new(http_client)
|
38
|
+
end
|
39
|
+
|
29
40
|
def graph_transfer
|
30
|
-
@graph_transfer ||= HTTP::GraphTransfer.new
|
31
|
-
|
32
|
-
|
41
|
+
@graph_transfer ||= HTTP::GraphTransfer.new(user_agent)
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_credentials(username, password)
|
45
|
+
user_agent.keychain.add(url, username, password)
|
33
46
|
end
|
34
47
|
|
35
48
|
def source_rigor
|
@@ -54,11 +67,13 @@ module RoadForest
|
|
54
67
|
end
|
55
68
|
|
56
69
|
def putting(&block)
|
70
|
+
|
57
71
|
graph = build_graph_store
|
58
72
|
access = RDF::UpdateManager.new
|
59
73
|
access.rigor = source_rigor
|
60
74
|
access.source_graph = graph
|
61
75
|
updater = RDF::GraphFocus.new(access, url)
|
76
|
+
|
62
77
|
annealer = RDF::SourceRigor::CredenceAnnealer.new(graph)
|
63
78
|
|
64
79
|
annealer.resolve do
|
@@ -75,11 +90,13 @@ module RoadForest
|
|
75
90
|
|
76
91
|
def posting(&block)
|
77
92
|
require 'roadforest/rdf/post-focus'
|
93
|
+
|
78
94
|
graph = build_graph_store
|
79
95
|
access = RDF::PostManager.new
|
80
96
|
access.rigor = source_rigor
|
81
97
|
access.source_graph = graph
|
82
98
|
poster = RDF::PostFocus.new(access, url)
|
99
|
+
|
83
100
|
graphs = {}
|
84
101
|
poster.graphs = graphs
|
85
102
|
|
@@ -91,6 +108,7 @@ module RoadForest
|
|
91
108
|
end
|
92
109
|
|
93
110
|
def getting(&block)
|
111
|
+
|
94
112
|
graph = build_graph_store
|
95
113
|
access = RDF::ReadOnlyManager.new
|
96
114
|
access.rigor = source_rigor
|
@@ -106,10 +124,7 @@ module RoadForest
|
|
106
124
|
elsif destination.respond_to?(:to_s)
|
107
125
|
destination = destination.to_s
|
108
126
|
end
|
109
|
-
|
110
|
-
request.body = io
|
111
|
-
request.headers["Content-Type"] = type
|
112
|
-
response = http_client.do_request request
|
127
|
+
response = user_agent.make_request("PUT", destination, {"Content-Type" => type}, io)
|
113
128
|
end
|
114
129
|
|
115
130
|
#TODO:
|
@@ -6,7 +6,7 @@ module RoadForest
|
|
6
6
|
def self.registry_purpose; "resource type"; end
|
7
7
|
extend Utility::ClassRegistry::Registrar
|
8
8
|
|
9
|
-
module RDF
|
9
|
+
module RDF #XXX This shouldn't be in RDF - resource roles are on the REST side
|
10
10
|
#Used for a resource that presents a read-only representation
|
11
11
|
class ReadOnly < Webmachine::Resource
|
12
12
|
def self.register(method_name)
|
@@ -84,6 +84,20 @@ module RoadForest
|
|
84
84
|
|
85
85
|
def content_types_provided
|
86
86
|
model.type_handling.renderers.type_map
|
87
|
+
rescue => ex
|
88
|
+
super
|
89
|
+
end
|
90
|
+
|
91
|
+
def is_authorized?(header)
|
92
|
+
@authorization = @model.authorization(request.method, header)
|
93
|
+
if(@authorization == :public || @authorization == :granted)
|
94
|
+
return true
|
95
|
+
end
|
96
|
+
@model.authentication_challenge
|
97
|
+
end
|
98
|
+
|
99
|
+
#Add cache-control headers here
|
100
|
+
def finish_request
|
87
101
|
end
|
88
102
|
|
89
103
|
def resource_exists?
|
@@ -0,0 +1,13 @@
|
|
1
|
+
!!! XML
|
2
|
+
!!! 5
|
3
|
+
%html{:xmlns => "http://www.w3.org/1999/xhtml", :lang => lang, :prefix => prefix}
|
4
|
+
- if base || title
|
5
|
+
%head
|
6
|
+
- if base
|
7
|
+
%base{:href => base}
|
8
|
+
- if title
|
9
|
+
%title= title
|
10
|
+
%body
|
11
|
+
- subjects.each do |subject|
|
12
|
+
%div{subject.attrs}
|
13
|
+
!= yield(subject)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
- if heading_predicates.include?(predicate) && object.literal?
|
2
|
+
%h1{object.simple_attrs}= escape_entities(get_value(object.object))
|
3
|
+
- else
|
4
|
+
%div.property
|
5
|
+
%span.label
|
6
|
+
= get_predicate_name(predicate)
|
7
|
+
- if object.is_subject?
|
8
|
+
%div{object.attrs}= yield object
|
9
|
+
- elsif object.attrs.empty?
|
10
|
+
= yield object
|
11
|
+
- else
|
12
|
+
%span{object.attrs}<= yield object
|
@@ -0,0 +1,20 @@
|
|
1
|
+
!!! XML
|
2
|
+
!!! 5
|
3
|
+
%html{:xmlns => "http://www.w3.org/1999/xhtml", :lang => lang, :prefix => prefix}
|
4
|
+
- if base || title
|
5
|
+
%head
|
6
|
+
- if base
|
7
|
+
%base{:href => base}
|
8
|
+
- if title
|
9
|
+
%title= title
|
10
|
+
%link{:rel => "stylesheet", :href => "http://rdf.kellogg-assoc.com/css/distiller.css", :type => "text/css"}
|
11
|
+
%script{:src => "https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js", :type => "text/javascript"}
|
12
|
+
%script{:src => "http://rdf.kellogg-assoc.com/js/distiller.js", :type => "text/javascript"}
|
13
|
+
%body
|
14
|
+
- if base
|
15
|
+
%p= "RDFa serialization URI base: <#{base}>"
|
16
|
+
- subjects.each do |subject|
|
17
|
+
%td{subject.attrs}
|
18
|
+
!= yield(subject)
|
19
|
+
%footer
|
20
|
+
%p= "Written by <a href='http://rubygems.org/gems/rdf-rdfa'>RDF::RDFa</a> version #{RDF::RDFa::VERSION}"
|
@@ -0,0 +1 @@
|
|
1
|
+
Empty
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
=get_curie(object)
|
@@ -0,0 +1 @@
|
|
1
|
+
#{escape_entities(get_value(object))}
|
@@ -0,0 +1 @@
|
|
1
|
+
%a{:property => get_curie(predicate), :href => object.to_s, :inlist => inlist}= object.to_s
|
@@ -0,0 +1 @@
|
|
1
|
+
!~ get_value(object)
|