roadforest 0.0.3 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|