rdf-ldp 0.9.2 → 2.1.0
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 +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
|
##
|