rdf-ldp 0.9.2 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +10 -0
- data/CREDITS +1 -0
- data/README.md +66 -10
- data/UNLICENSE +1 -1
- data/VERSION +1 -1
- data/app/lamprey.rb +119 -40
- data/bin/lamprey +15 -1
- data/lib/rack/ldp.rb +23 -25
- data/lib/rdf/ldp/container.rb +32 -22
- data/lib/rdf/ldp/direct_container.rb +9 -7
- data/lib/rdf/ldp/indirect_container.rb +8 -6
- data/lib/rdf/ldp/interaction_model.rb +39 -29
- data/lib/rdf/ldp/non_rdf_source.rb +10 -6
- data/lib/rdf/ldp/rdf_source.rb +3 -3
- data/lib/rdf/ldp/resource.rb +33 -31
- data/lib/rdf/ldp/spec/container.rb +337 -0
- data/lib/rdf/ldp/spec/direct_container.rb +245 -0
- data/lib/rdf/ldp/spec/indirect_container.rb +152 -0
- data/lib/rdf/ldp/spec/non_rdf_source.rb +138 -0
- data/lib/rdf/ldp/spec/rdf_source.rb +370 -0
- data/lib/rdf/ldp/spec/resource.rb +242 -0
- data/lib/rdf/ldp/spec.rb +6 -0
- data/lib/rdf/ldp/storage_adapters/file_storage_adapter.rb +4 -4
- data/lib/rdf/ldp/version.rb +16 -3
- data/lib/rdf/ldp.rb +4 -3
- metadata +89 -55
@@ -13,9 +13,9 @@ module RDF::LDP
|
|
13
13
|
# Acceptable`. LDP-NR's cannot be added since indirect membership is not well
|
14
14
|
# defined for them, per _LDP 5.5.1.2_.
|
15
15
|
#
|
16
|
-
# @see
|
16
|
+
# @see https://www.w3.org/TR/ldp/#h-ldpic-indirectmbr for an explanation if
|
17
17
|
# indirect membership and limitiations surrounding LDP-NRs.
|
18
|
-
# @see
|
18
|
+
# @see https://www.w3.org/TR/ldp/#dfn-linked-data-platform-indirect-container
|
19
19
|
# definition of LDP Indirect Container
|
20
20
|
class IndirectContainer < DirectContainer
|
21
21
|
INSERTED_CONTENT_REL_URI = RDF::Vocab::LDP.insertedContentRelation.freeze
|
@@ -61,7 +61,7 @@ module RDF::LDP
|
|
61
61
|
# @raise [RDF::LDP::NotAcceptable] if multiple inserted content relations
|
62
62
|
# exist.
|
63
63
|
#
|
64
|
-
# @see
|
64
|
+
# @see https://www.w3.org/TR/ldp/#dfn-membership-triples
|
65
65
|
def inserted_content_relation
|
66
66
|
statements = inserted_content_statements
|
67
67
|
return statements.first.object if statements.count == 1
|
@@ -87,9 +87,11 @@ module RDF::LDP
|
|
87
87
|
predicate = inserted_content_relation
|
88
88
|
return resource.to_uri if predicate == MEMBER_SUBJECT_URI
|
89
89
|
|
90
|
-
|
91
|
-
|
92
|
-
|
90
|
+
if resource.non_rdf_source?
|
91
|
+
raise(NotAcceptable, "#{resource.to_uri} is an LDP-NR; cannot add " \
|
92
|
+
'it to an IndirectContainer with a content ' \
|
93
|
+
'relation.')
|
94
|
+
end
|
93
95
|
|
94
96
|
target = transaction || resource.graph
|
95
97
|
statements = target.query([resource.subject_uri, predicate, :o])
|
@@ -1,14 +1,16 @@
|
|
1
1
|
module RDF
|
2
2
|
module LDP
|
3
|
+
##
|
4
|
+
# Provides an interaction model registry.
|
3
5
|
class InteractionModel
|
4
6
|
class << self
|
5
7
|
##
|
6
8
|
# Interaction models are in reverse order of preference for POST/PUT
|
7
|
-
# requests; e.g. if a client sends a request with Resource, RDFSource,
|
8
|
-
# BasicContainer headers, the server gives a basic container.
|
9
|
+
# requests; e.g. if a client sends a request with Resource, RDFSource,
|
10
|
+
# oand BasicContainer headers, the server gives a basic container.
|
9
11
|
#
|
10
|
-
# Interaction models are initialized in the correct order, but with no
|
11
|
-
# registered to handle them.
|
12
|
+
# Interaction models are initialized in the correct order, but with no
|
13
|
+
# class registered to handle them.
|
12
14
|
@@interaction_models = {
|
13
15
|
RDF::LDP::RDFSource.to_uri => nil,
|
14
16
|
RDF::LDP::Container.to_uri => nil,
|
@@ -17,43 +19,47 @@ module RDF
|
|
17
19
|
RDF::LDP::IndirectContainer.to_uri => nil,
|
18
20
|
RDF::LDP::NonRDFSource.to_uri => nil
|
19
21
|
}
|
20
|
-
|
22
|
+
|
21
23
|
##
|
22
|
-
# Register a new interaction model for one or more Link header URIs.
|
23
|
-
# will automatically be registered.
|
24
|
+
# Register a new interaction model for one or more Link header URIs.
|
25
|
+
# klass.to_uri will automatically be registered.
|
24
26
|
#
|
25
|
-
# @param [RDF::LDP::Resource] klass the implementation class to
|
27
|
+
# @param [RDF::LDP::Resource] klass the implementation class to
|
28
|
+
# register
|
26
29
|
# @param [Hash <Symbol, *>] opts registration options:
|
27
|
-
# :default [true, false] if true, klass will become the new default
|
28
|
-
# unrecognized Link headers
|
29
|
-
# :for [RDF::URI, Array<RDF::URI>] additional URIs for which klass
|
30
|
-
# the interaction model
|
30
|
+
# :default [true, false] if true, klass will become the new default
|
31
|
+
# klass for unrecognized Link headers
|
32
|
+
# :for [RDF::URI, Array<RDF::URI>] additional URIs for which klass
|
33
|
+
# should become the interaction model
|
31
34
|
#
|
32
35
|
# @return [RDF::LDP::Resource] klass
|
33
|
-
def register(klass, opts={})
|
36
|
+
def register(klass, opts = {})
|
34
37
|
unless klass.ancestors.include?(RDF::LDP::Resource)
|
35
|
-
raise ArgumentError,
|
38
|
+
raise ArgumentError,
|
39
|
+
'Interaction models must subclass `RDF::LDP::Resource`'
|
36
40
|
end
|
37
|
-
@@default = klass if opts[:default]
|
41
|
+
@@default = klass if opts[:default] || @@default.nil?
|
38
42
|
@@interaction_models[klass.to_uri] = klass
|
39
43
|
Array(opts[:for]).each do |model|
|
40
44
|
@@interaction_models[model] = klass
|
41
45
|
end
|
42
46
|
klass
|
43
47
|
end
|
44
|
-
|
48
|
+
|
45
49
|
##
|
46
|
-
# Find the appropriate interaction model given a set of Link header
|
50
|
+
# Find the appropriate interaction model given a set of Link header
|
51
|
+
# URIs.
|
47
52
|
#
|
48
53
|
# @param [Array<RDF::URI>] uris
|
49
54
|
#
|
50
|
-
# @return [Class] a subclass of {RDF::LDP::Resource} that most narrowly
|
51
|
-
# supplied `uris`, or the default interaction model if
|
55
|
+
# @return [Class] a subclass of {RDF::LDP::Resource} that most narrowly
|
56
|
+
# matches the supplied `uris`, or the default interaction model if
|
57
|
+
# nothing matches
|
52
58
|
def find(uris)
|
53
|
-
match = @@interaction_models.keys.reverse.find { |u| uris.include? u }
|
59
|
+
match = @@interaction_models.keys.reverse.find { |u| uris.include? u }
|
54
60
|
self.for(match) || @@default
|
55
61
|
end
|
56
|
-
|
62
|
+
|
57
63
|
##
|
58
64
|
# Find the interaction model registered for a given uri
|
59
65
|
#
|
@@ -63,23 +69,27 @@ module RDF
|
|
63
69
|
def for(uri)
|
64
70
|
@@interaction_models[uri]
|
65
71
|
end
|
66
|
-
|
72
|
+
|
67
73
|
##
|
68
74
|
# The default registered interaction model
|
69
75
|
def default
|
70
76
|
@@default
|
71
77
|
end
|
72
|
-
|
78
|
+
|
73
79
|
##
|
74
|
-
# Test an array of URIs to see if their interaction models are
|
75
|
-
# refer either to RDF models or
|
80
|
+
# Test an array of URIs to see if their interaction models are
|
81
|
+
# compatible (e.g., all of the URIs refer either to RDF models or
|
82
|
+
# non-RDF models, but not a combination of both).
|
76
83
|
#
|
77
84
|
# @param [Array<RDF::URI>] uris
|
78
|
-
# @return [TrueClass or FalseClass] true if the models specified by
|
85
|
+
# @return [TrueClass or FalseClass] true if the models specified by
|
86
|
+
# `uris` are compatible
|
79
87
|
def compatible?(uris)
|
80
|
-
classes
|
81
|
-
(rdf,non_rdf) =
|
82
|
-
|
88
|
+
classes = uris.collect { |m| self.for(m) }
|
89
|
+
(rdf, non_rdf) =
|
90
|
+
classes.compact.partition { |c| c.ancestors.include?(RDFSource) }
|
91
|
+
|
92
|
+
rdf.empty? || non_rdf.empty?
|
83
93
|
end
|
84
94
|
end
|
85
95
|
end
|
@@ -15,7 +15,7 @@ module RDF::LDP
|
|
15
15
|
# the resource itself is returned by `#description`.
|
16
16
|
#
|
17
17
|
# @see RDF::LDP::Resource
|
18
|
-
# @see
|
18
|
+
# @see https://www.w3.org/TR/ldp/#dfn-linked-data-platform-non-rdf-source for
|
19
19
|
# a definition of NonRDFSource in LDP
|
20
20
|
class NonRDFSource < Resource
|
21
21
|
attr_reader :storage
|
@@ -27,10 +27,14 @@ module RDF::LDP
|
|
27
27
|
FORMAT_TERM = RDF::Vocab::DC11.format.freeze
|
28
28
|
|
29
29
|
##
|
30
|
-
# @param [
|
30
|
+
# @param [RDF::URI] subject_uri
|
31
|
+
# @param [RDF::Queryable] data
|
32
|
+
# @param [StorageAdapter] storage_adapter a class implementing the StorageAdapter interface
|
31
33
|
#
|
32
34
|
# @see RDF::LDP::Resource#initialize
|
33
|
-
def initialize(subject_uri,
|
35
|
+
def initialize(subject_uri,
|
36
|
+
data = RDF::Repository.new,
|
37
|
+
storage_adapter = DEFAULT_ADAPTER)
|
34
38
|
data ||= RDF::Repository.new # allows explict `nil` pass
|
35
39
|
@storage = storage_adapter.new(self)
|
36
40
|
super(subject_uri, data)
|
@@ -40,7 +44,7 @@ module RDF::LDP
|
|
40
44
|
# @return [RDF::URI] uri with lexical representation
|
41
45
|
# 'http://www.w3.org/ns/ldp#NonRDFSource'
|
42
46
|
#
|
43
|
-
# @see
|
47
|
+
# @see https://www.w3.org/TR/ldp/#dfn-linked-data-platform-non-rdf-source
|
44
48
|
def self.to_uri
|
45
49
|
RDF::Vocab::LDP.NonRDFSource
|
46
50
|
end
|
@@ -109,7 +113,7 @@ module RDF::LDP
|
|
109
113
|
##
|
110
114
|
# Sets the MIME type for the resource in `metagraph`.
|
111
115
|
#
|
112
|
-
# @param [String] a string representing the content type for this LDP-NR.
|
116
|
+
# @param [String] content_type a string representing the content type for this LDP-NR.
|
113
117
|
# This SHOULD be a regisered MIME type.
|
114
118
|
#
|
115
119
|
# @return [StorageAdapter] the content type
|
@@ -132,7 +136,7 @@ module RDF::LDP
|
|
132
136
|
#
|
133
137
|
# @raise [RDF::LDP::RequestError] when the request fails
|
134
138
|
def to_response
|
135
|
-
|
139
|
+
exists? && !destroyed? ? storage.io : []
|
136
140
|
end
|
137
141
|
|
138
142
|
private
|
data/lib/rdf/ldp/rdf_source.rb
CHANGED
@@ -25,7 +25,7 @@ module RDF::LDP
|
|
25
25
|
# but MAY be absent from (or in conflict with) the representation of its
|
26
26
|
# state in `#graph`.
|
27
27
|
#
|
28
|
-
# @see
|
28
|
+
# @see https://www.w3.org/TR/ldp/#dfn-linked-data-platform-rdf-source
|
29
29
|
# Definition of ldp:RDFSource in the LDP specification
|
30
30
|
class RDFSource < Resource
|
31
31
|
class << self
|
@@ -33,7 +33,7 @@ module RDF::LDP
|
|
33
33
|
# @return [RDF::URI] uri with lexical representation
|
34
34
|
# 'http://www.w3.org/ns/ldp#RDFSource'
|
35
35
|
#
|
36
|
-
# @see
|
36
|
+
# @see https://www.w3.org/TR/ldp/#dfn-linked-data-platform-rdf-source
|
37
37
|
def to_uri
|
38
38
|
RDF::Vocab::LDP.RDFSource
|
39
39
|
end
|
@@ -241,7 +241,7 @@ module RDF::LDP
|
|
241
241
|
#
|
242
242
|
# @raise [RDF::LDP::UnsupportedMediaType] if no appropriate reader is found
|
243
243
|
#
|
244
|
-
# @see
|
244
|
+
# @see https://www.rubydoc.info/github/rack/rack/file/SPEC#The_Input_Stream
|
245
245
|
# Documentation on input streams in the Rack SPEC
|
246
246
|
def parse_graph(input, content_type)
|
247
247
|
reader = RDF::Reader.for(content_type: content_type.to_s)
|
data/lib/rdf/ldp/resource.rb
CHANGED
@@ -38,14 +38,15 @@ module RDF::LDP
|
|
38
38
|
# resource.exists? # => true
|
39
39
|
# resource.metagraph.dump :ttl
|
40
40
|
# # => "<http://example.org/moomin> a <http://www.w3.org/ns/ldp#Resource>;
|
41
|
-
#
|
41
|
+
# # <http://purl.org/dc/terms/modified>
|
42
|
+
# # "2015-10-25T14:24:56-07:00"^^xsd:dateTime ."
|
42
43
|
#
|
43
44
|
# @example updating a Resource updates the `#last_modified` date
|
44
45
|
# resource.last_modified
|
45
|
-
# # => #<DateTime: 2015-10-25T14:32:01-07:00
|
46
|
+
# # => #<DateTime: 2015-10-25T14:32:01-07:00...>
|
46
47
|
# resource.update('blah', 'text/plain')
|
47
48
|
# resource.last_modified
|
48
|
-
# # => #<DateTime: 2015-10-25T14:32:04-07:00
|
49
|
+
# # => #<DateTime: 2015-10-25T14:32:04-07:00...>
|
49
50
|
#
|
50
51
|
# @example destroying a Resource
|
51
52
|
# resource.exists? # => true
|
@@ -75,13 +76,13 @@ module RDF::LDP
|
|
75
76
|
# #<RDF::LDP::Resource:0x00564f4a646028
|
76
77
|
# @data=#<RDF::Repository:0x2b27a5391708()>,
|
77
78
|
# @exists=true,
|
78
|
-
# @metagraph=#<RDF::Graph:
|
79
|
-
# @subject_uri=#<RDF::URI:
|
79
|
+
# @metagraph=#<RDF::Graph:0xea7(http://example.org/moomin#meta)>,
|
80
|
+
# @subject_uri=#<RDF::URI:0xea8 URI:http://example.org/moomin>>]
|
80
81
|
#
|
81
82
|
# resource.request(:put, 200, {}, {}) # RDF::LDP::MethodNotAllowed: put
|
82
83
|
#
|
83
|
-
# @see
|
84
|
-
# @see
|
84
|
+
# @see https://www.w3.org/TR/ldp/ Linked Data platform Specification
|
85
|
+
# @see https://www.w3.org/TR/ldp/#dfn-linked-data-platform-resource Definition
|
85
86
|
# of 'Resource' in LDP
|
86
87
|
class Resource
|
87
88
|
CONTAINS_URI = RDF::Vocab::LDP.contains.freeze
|
@@ -101,7 +102,7 @@ module RDF::LDP
|
|
101
102
|
# @return [RDF::URI] uri with lexical representation
|
102
103
|
# 'http://www.w3.org/ns/ldp#Resource'
|
103
104
|
#
|
104
|
-
# @see
|
105
|
+
# @see https://www.w3.org/TR/ldp/#dfn-linked-data-platform-resource
|
105
106
|
def to_uri
|
106
107
|
RDF::Vocab::LDP.Resource
|
107
108
|
end
|
@@ -109,7 +110,7 @@ module RDF::LDP
|
|
109
110
|
##
|
110
111
|
# Creates an unique id (URI Slug) for a resource.
|
111
112
|
#
|
112
|
-
# @note the current implementation uses
|
113
|
+
# @note the current implementation uses `SecureRandom#uuid`.
|
113
114
|
#
|
114
115
|
# @return [String] a unique ID
|
115
116
|
def gen_id
|
@@ -161,7 +162,7 @@ module RDF::LDP
|
|
161
162
|
.map { |link| RDF::URI.intern(link.href) }
|
162
163
|
|
163
164
|
return InteractionModel.default if models.empty?
|
164
|
-
|
165
|
+
|
165
166
|
raise NotAcceptable unless InteractionModel.compatible?(models)
|
166
167
|
|
167
168
|
InteractionModel.find(models)
|
@@ -202,9 +203,9 @@ module RDF::LDP
|
|
202
203
|
##
|
203
204
|
# @abstract creates the resource
|
204
205
|
#
|
205
|
-
# @param [IO, File]
|
206
|
+
# @param [IO, File] _input input (usually from a Rack env's
|
206
207
|
# `rack.input` key) used to determine the Resource's initial state.
|
207
|
-
# @param [#to_s]
|
208
|
+
# @param [#to_s] _content_type a MIME content_type used to interpret the
|
208
209
|
# input. This MAY be used as a content type for the created Resource
|
209
210
|
# (especially for `LDP::NonRDFSource`s).
|
210
211
|
#
|
@@ -302,13 +303,13 @@ module RDF::LDP
|
|
302
303
|
#
|
303
304
|
# @return [String] an HTTP Etag
|
304
305
|
#
|
305
|
-
# @note these etags are weak, but we allow clients to use them in
|
306
|
+
# @note these etags are weak, but we allow clients to use them in
|
306
307
|
# `If-Match` headers, and use weak comparison. This is in conflict with
|
307
|
-
# https://tools.ietf.org/html/rfc7232#section-3.1. See:
|
308
|
+
# https://tools.ietf.org/html/rfc7232#section-3.1. See:
|
308
309
|
# https://github.com/ruby-rdf/rdf-ldp/issues/68
|
309
310
|
#
|
310
|
-
# @see
|
311
|
-
# @see
|
311
|
+
# @see https://www.w3.org/TR/ldp#h-ldpr-gen-etags LDP ETag clause for GET
|
312
|
+
# @see https://www.w3.org/TR/ldp#h-ldpr-put-precond LDP ETag clause for PUT
|
312
313
|
# @see https://tools.ietf.org/html/rfc7232#section-2.1
|
313
314
|
# Weak vs. strong validators
|
314
315
|
def etag
|
@@ -392,7 +393,7 @@ module RDF::LDP
|
|
392
393
|
# Runs the request and returns the object's desired HTTP response body,
|
393
394
|
# conforming to the Rack interfare.
|
394
395
|
#
|
395
|
-
# @see
|
396
|
+
# @see https://www.rubydoc.info/github/rack/rack/master/file/SPEC#The_Body
|
396
397
|
# Rack body documentation
|
397
398
|
def to_response
|
398
399
|
[]
|
@@ -535,10 +536,10 @@ module RDF::LDP
|
|
535
536
|
# @return [Array<String>] an array of link headers to add to the
|
536
537
|
# existing ones
|
537
538
|
#
|
538
|
-
# @see
|
539
|
-
# @see
|
540
|
-
# @see
|
541
|
-
# @see
|
539
|
+
# @see https://www.w3.org/TR/ldp/#h-ldpr-gen-linktypehdr
|
540
|
+
# @see https://www.w3.org/TR/ldp/#h-ldprs-are-ldpr
|
541
|
+
# @see https://www.w3.org/TR/ldp/#h-ldpnr-type
|
542
|
+
# @see https://www.w3.org/TR/ldp/#h-ldpc-linktypehdr
|
542
543
|
def link_headers
|
543
544
|
return [] unless is_a? RDF::LDP::Resource
|
544
545
|
headers = [link_type_header(RDF::LDP::Resource.to_uri)]
|
@@ -558,24 +559,25 @@ module RDF::LDP
|
|
558
559
|
##
|
559
560
|
# Sets the last modified date/time to now
|
560
561
|
#
|
561
|
-
# @param transaction [RDF::Transaction] the transaction scope in which to
|
562
|
+
# @param transaction [RDF::Transaction] the transaction scope in which to
|
562
563
|
# apply changes. If none (or `nil`) is given, the change is made outside
|
563
564
|
# any transaction scope.
|
564
565
|
def set_last_modified(transaction = nil)
|
565
|
-
|
566
|
-
# transactions do not support updates or pattern deletes, so we must
|
567
|
-
# ask the Repository for the current last_modified to delete the statement
|
568
|
-
# transactionally
|
566
|
+
return metagraph.update([subject_uri, MODIFIED_URI, DateTime.now]) unless
|
569
567
|
transaction
|
570
|
-
.delete RDF::Statement(subject_uri, MODIFIED_URI, last_modified,
|
571
|
-
graph_name: metagraph_name) if last_modified
|
572
568
|
|
569
|
+
# transactions do not support updates or pattern deletes, so we must
|
570
|
+
# ask the Repository for the current last_modified to delete the
|
571
|
+
# statement transactionally
|
572
|
+
if last_modified
|
573
573
|
transaction
|
574
|
-
.
|
574
|
+
.delete RDF::Statement(subject_uri, MODIFIED_URI, last_modified,
|
575
575
|
graph_name: metagraph_name)
|
576
|
-
else
|
577
|
-
metagraph.update([subject_uri, MODIFIED_URI, DateTime.now])
|
578
576
|
end
|
577
|
+
|
578
|
+
transaction
|
579
|
+
.insert RDF::Statement(subject_uri, MODIFIED_URI, DateTime.now,
|
580
|
+
graph_name: metagraph_name)
|
579
581
|
end
|
580
582
|
|
581
583
|
##
|