rdf-ldp 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 76067ed8fd6b28e3c35eb9bcb65d14585cd4e0fe
4
- data.tar.gz: 098810c5faf3aca4dd7bc0992797eb1d4d309c67
3
+ metadata.gz: 96681a7e685f8af473a2bfe9a2b25ffdb147a057
4
+ data.tar.gz: 80a4b2e3efa3ec7aa7c41a0c6f252d67431babc0
5
5
  SHA512:
6
- metadata.gz: 0fcb758fa163d0c6e229af2286f7332fd1561df33f56f23786e36de7a524dcfe6cb7457f0c26e06faf9cf6d7519f56df58a9b82958ff9e8e7e0398365cba6fe1
7
- data.tar.gz: 465af32bf8ca54109cf01b555d626cc6e229ef83ffabc30d69eebd5ad1142697f3624cfbc38e618e1acf03b3b684d0e070a4bb36a157aba97ff802c10940d1f3
6
+ metadata.gz: fd5442ef53e136c2dc83bc6fc5cb2d4c35b66d6e2be7669abd1c7d6eb5888d4fa5ecae8ccdfeb888d6e702675dbe1e2cab6f98ff01cd142c03f8baf826b86c30
7
+ data.tar.gz: 1acbe59831cd7219ffb933957c96d67cbc16b21c4cd949d81b60f60201cba4c88490a4c2ab1897bae69a249858e2db77f4febe6c99a293da621de2e9b08a62d6
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ * Tom Johnson <tom@dp.la>
data/CREDITS ADDED
File without changes
data/README.md CHANGED
@@ -1,37 +1,59 @@
1
- # DEPRECATED - use rdf-vocab
1
+ RDF::LDP
2
+ ========
2
3
 
3
- This gem is deprecated; rdf-vocab gem (https://github.com/ruby-rdf/rdf-vocab, included in the linkeddata gem) now contains RDF::Vocab::LDP. You can find the helper method to strip LDP triples from an RDF::Graph object in the triannon gem in lib/oa_graph_helper.rb.
4
+ [![Build Status](https://travis-ci.org/ruby-rdf/rdf-ldp.svg?branch=develop)](https://travis-ci.org/ruby-rdf/rdf-ldp)
4
5
 
5
- # rdf-ldp
6
+ This software ships with the following libraries:
6
7
 
7
- [![Build Status](https://travis-ci.org/sul-dlss/rdf-ldp.svg)](https://travis-ci.org/sul-dlss/rdf-ldp) [![Dependency Status](https://gemnasium.com/sul-dlss/rdf-ldp.svg)](https://gemnasium.com/sul-dlss/rdf-ldp) [![Gem Version](https://badge.fury.io/rb/rdf-ldp.svg)](http://badge.fury.io/rb/rdf-ldp)
8
+ - `RDF::LDP` --- contains the domain model for LDP Resources.
9
+ - `Rack::LDP` --- a suite of Rack middleware for creating LDP servers based on
10
+ `RDF::LDP`.
11
+ - Lamprey --- a basic LDP server implemented with `Rack::LDP`.
8
12
 
9
- Contains vocabularies to be used by RDF ruby gem https://github.com/ruby-rdf/rdf/ to simplify coding when using LDP data.
13
+ Lamprey
14
+ =======
10
15
 
11
- Also contains helper method to strip LDP triples from an RDF::Graph object.
16
+ Lamprey is a basic LDP server. To start it, use:
12
17
 
13
- ## Installation
14
-
15
- Add this line to your application's Gemfile:
16
-
17
- ```ruby
18
- gem 'rdf-vocab' # (was rdf-ldp)
18
+ ```
19
+ bundle exec ruby app/lamprey.rb
19
20
  ```
20
21
 
21
- And then execute:
22
+ An `ldp:BasicContainer` will be created at the address of your first
23
+ `GET` request. Note that if that request is made to the server root,
24
+ Sinatra will assume a trailing slash.
25
+
26
+ ```bash
27
+ $ curl -i http://localhost:4567
28
+
29
+ HTTP/1.1 200 OK
30
+ Content-Type: text/turtle
31
+ Link: <http://www.w3.org/ns/ldp#Resource>;rel="type",<http://www.w3.org/ns/ldp#RDFSource>;rel="type",<http://www.w3.org/ns/ldp#BasicContainer>;rel="type"
32
+ Allow: GET, POST, PUT, DELETE, OPTIONS, HEAD
33
+ Accept-Post: application/n-triples, text/plain, application/n-quads, text/x-nquads, application/ld+json, application/x-ld+json, application/rdf+json, text/html, text/n3, text/rdf+n3, application/rdf+n3, application/rdf+xml, text/csv, text/tab-separated-values, application/csvm+json, text/turtle, text/rdf+turtle, application/turtle, application/x-turtle, application/trig, application/x-trig, application/trix
34
+ Etag: "1B2M2Y8AsgTpgAmY7PhCfg==0"
35
+ Vary: Accept
36
+ X-Content-Type-Options: nosniff
37
+ Server: WEBrick/1.3.1 (Ruby/2.1.0/2013-12-25)
38
+ Date: Mon, 27 Jul 2015 23:19:06 GMT
39
+ Content-Length: 0
40
+ Connection: Keep-Alive
41
+ ```
22
42
 
23
- $ bundle
43
+ Compliance
44
+ ----------
24
45
 
25
- Or install it yourself as:
46
+ Current compliance reports for Lamprey are located in [/reports](reports/).
47
+ Reports are generated by the LDP test suite. To duplicate the results,
48
+ use the `testsuite` branch, which contains a work-around for
49
+ [w3c/ldp-testsuite#224](https://github.com/w3c/ldp-testsuite/issues/224).
26
50
 
27
- $ gem install rdf-vocab # (was rdf-ldp)
51
+ License
52
+ ========
28
53
 
29
- ## Usage
54
+ This software is released under a public domain waiver (Unlicense).
30
55
 
31
- require 'rdf/vocab'
56
+
32
57
 
33
- RDF::Vocab::LDP.member #=> RDF::URI("http://www.w3.org/ns/ldp#member")
34
58
 
35
- # DEPRECATED - use rdf-vocab
36
59
 
37
- This gem is deprecated; rdf-vocab gem (https://github.com/ruby-rdf/rdf-vocab, included in the linkeddata gem) now contains RDF::Vocab::LDP. You can find the helper method to strip LDP triples from an RDF::Graph object in the triannon gem in lib/oa_graph_helper.rb.
data/UNLICENSE ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
data/lib/rack/ldp.rb ADDED
@@ -0,0 +1,121 @@
1
+ require 'rack'
2
+ begin
3
+ require 'linkeddata'
4
+ rescue LoadError => e
5
+ require 'rdf/turtle'
6
+ require 'json/ld'
7
+ end
8
+
9
+ require 'rack/linkeddata'
10
+ require 'rdf/ldp'
11
+
12
+ module Rack
13
+ ##
14
+ # Provides Rack middleware for handling Linked Data Platform requirements
15
+ # when passed {RDF::LDP::Resource} and its subclasses as response objects.
16
+ #
17
+ # Response objects that are not an {RDF::LDP::Resource} are passed over
18
+ # without alteration, allowing server implementers to mix LDP interaction
19
+ # patterns with others on the same server.
20
+ #
21
+ # The suite can be mix-and-matched as needed. This allows easy swap in of
22
+ # custom handlers for parts of the behavior. It is recommended that you use
23
+ # {Rack::LDP::ContentNegotiation}, {Rack::LDP::Errors}, and
24
+ # {Rack::LDP::Responses} as the outer three services. With these in place,
25
+ # you can handle requests as needed in your application, giving responses
26
+ # conforming to the core {RDF::LDP::Resource} interface.
27
+ #
28
+ # @example
29
+ # run Rack:;Builder.new do
30
+ # use Rack::LDP::ContentNegotiation
31
+ # use Rack::LDP::Errors
32
+ # use Rack::LDP::Responses
33
+ # # ...
34
+ # end
35
+ #
36
+ # @see http://www.w3.org/TR/ldp/ the LDP specification
37
+ module LDP
38
+ ##
39
+ # Catches and handles RequestErrors thrown by RDF::LDP
40
+ class Errors
41
+ ##
42
+ # @param [#call] app
43
+ def initialize(app)
44
+ @app = app
45
+ end
46
+
47
+ ##
48
+ # Catches {RDF::LDP::RequestError} and its various subclasses, building an
49
+ # appropriate response
50
+ #
51
+ # @param [Array] env a rack env array
52
+ # @return [Array] a rack env array with added headers
53
+ def call(env)
54
+ begin
55
+ @app.call(env)
56
+ rescue RDF::LDP::RequestError => err
57
+ return [err.status, err.headers, [err.message]]
58
+ end
59
+ end
60
+ end
61
+
62
+ ##
63
+ # Converts RDF::LDP::Resource} into appropriate responses
64
+ class Responses
65
+ ##
66
+ # @param [#call] app
67
+ def initialize(app)
68
+ @app = app
69
+ end
70
+
71
+ ##
72
+ # Converts the response body from {RDF::LDP::Resource} form to a Graph
73
+ def call(env)
74
+ status, headers, response = @app.call(env)
75
+
76
+ if response.is_a? RDF::LDP::Resource
77
+ new_response = response.to_response
78
+ response.close if response.respond_to? :close
79
+ response = new_response
80
+ end
81
+
82
+ [status, headers, response]
83
+ end
84
+ end
85
+
86
+ ##
87
+ #
88
+ class Requests
89
+ ##
90
+ # @param [#call] app
91
+ def initialize(app)
92
+ @app = app
93
+ end
94
+
95
+ ##
96
+ # Handles a Rack protocol request. Sends appropriate request to the
97
+ # object, alters response accordingly.
98
+ #
99
+ # @param [Array] env a rack env array
100
+ # @return [Array] a rack env array with added headers
101
+ def call(env)
102
+ status, headers, response = @app.call(env)
103
+ return [status, headers, response] unless
104
+ response.is_a? RDF::LDP::Resource
105
+
106
+ response
107
+ .send(:request, env['REQUEST_METHOD'].to_sym, status, headers, env)
108
+ end
109
+ end
110
+
111
+ ##
112
+ # Specializes {Rack::LinkedData::ContentNegotiation}, making the default
113
+ # return type 'text/turtle'
114
+ class ContentNegotiation < Rack::LinkedData::ContentNegotiation
115
+ def initialize(app, options = {})
116
+ options[:default] ||= 'text/turtle'
117
+ super
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,143 @@
1
+ module RDF::LDP
2
+ class Container < RDFSource
3
+ ##
4
+ # @return [RDF::URI] uri with lexical representation
5
+ # 'http://www.w3.org/ns/ldp#Container'
6
+ def self.to_uri
7
+ RDF::Vocab::LDP.Container
8
+ end
9
+
10
+ ##
11
+ # @return [Boolean] whether this is an ldp:Container
12
+ def container?
13
+ true
14
+ end
15
+
16
+ ##
17
+ # @return [RDF::URI] a URI representing the container type
18
+ def container_class
19
+ CONTAINER_CLASSES[:basic]
20
+ end
21
+
22
+ ##
23
+ # @see RDFSource#create
24
+ def create(input, content_type)
25
+ super { |statements| validate_triples!(statements) }
26
+ end
27
+
28
+ ##
29
+ # @see RDFSource#update
30
+ def update(input, content_type)
31
+ super { |statements| validate_triples!(statements) }
32
+ end
33
+
34
+ ##
35
+ # Adds a member `resource` to the container. Handles containment and
36
+ # membership triples as appropriate for the container type.
37
+ #
38
+ # @param [RDF::Term] a new member for this container
39
+ # @return [Container] self
40
+ def add(resource)
41
+ add_containment_triple(resource.to_uri)
42
+ end
43
+
44
+ ##
45
+ # Removes a member `resource` from the container. Handles containment and
46
+ # membership triples as appropriate for the container type.
47
+ #
48
+ # @param [RDF::Term] a new member for this container
49
+ # @return [Container] self
50
+ def remove(resource)
51
+ remove_containment_triple(resource.to_uri)
52
+ end
53
+
54
+ ##
55
+ # @return [RDF::Query::Enumerator] the containment triples
56
+ def containment_triples
57
+ graph.query([subject_uri,
58
+ RDF::Vocab::LDP.contains,
59
+ nil]).statements
60
+ end
61
+
62
+ ##
63
+ # @param [RDF::Statement] statement
64
+ #
65
+ # @return [Boolean] true if the containment triple exists
66
+ #
67
+ # @todo for some reason `#include?` doesn't work! figure out why, this is
68
+ # clumsy.
69
+ def has_containment_triple?(statement)
70
+ !(containment_triples.select { |t| statement == t }.empty?)
71
+ end
72
+
73
+ ##
74
+ # Adds a containment triple for `resource` to the container's `#graph`.
75
+ #
76
+ # @param [RDF::Term] a new member for this container
77
+ # @return [Container] self
78
+ def add_containment_triple(resource)
79
+ graph << make_containment_triple(resource)
80
+ self
81
+ end
82
+
83
+ ##
84
+ # Remove a containment triple for `resource` to the container's `#graph`.
85
+ #
86
+ # @param [RDF::Term] a member to remove from this container
87
+ # @return [Container] self
88
+ def remove_containment_triple(resource)
89
+ graph.delete(make_containment_triple(resource))
90
+ self
91
+ end
92
+
93
+ ##
94
+ # @param [RDF::Term] a member for this container
95
+ #
96
+ # @return [RDF::URI] the containment triple
97
+ def make_containment_triple(resource)
98
+ RDF::Statement(subject_uri, RDF::Vocab::LDP.contains, resource)
99
+ end
100
+
101
+ private
102
+
103
+ ##
104
+ # Handles a POST request. Parses a graph in the body of `env` and treats all
105
+ # statements in that graph (irrespective of any graph names) as constituting
106
+ # the initial state of the created source.
107
+ #
108
+ # @raise [RDF::LDP::RequestError] when creation fails
109
+ #
110
+ # @return [Array<Fixnum, Hash<String, String>, #each] a new Rack response
111
+ # array.
112
+ def post(status, headers, env)
113
+ klass = self.class.interaction_model(env.fetch('HTTP_LINK', ''))
114
+ slug = env['HTTP_SLUG']
115
+ slug = klass.gen_id if slug.nil? || slug.empty?
116
+ raise NotAcceptable.new('Refusing to create resource with `#` in Slug') if
117
+ slug.include? '#'
118
+
119
+ id = (subject_uri / slug).canonicalize
120
+
121
+ created = klass.new(id, @data)
122
+ .create(env['rack.input'], env['CONTENT_TYPE'])
123
+
124
+ add(created)
125
+ headers['Location'] = created.subject_uri.to_s
126
+ [201, created.send(:update_headers, headers), created]
127
+ end
128
+
129
+ def validate_triples!(statements)
130
+ existing_triples = containment_triples.to_a
131
+ statements.query(subject: subject_uri,
132
+ predicate: RDF::Vocab::LDP.contains) do |statement|
133
+ existing_triples.delete(statement) do
134
+ raise Conflict.new('Attempted to write unacceptable LDP ' \
135
+ "containment-triple: #{statement}")
136
+ end
137
+ end
138
+ raise Conflict.new('Cannot remove containment triples in updates. ' \
139
+ "Attepted to remove #{existing_triples}") unless
140
+ existing_triples.empty?
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,112 @@
1
+ module RDF::LDP
2
+ class DirectContainer < Container
3
+ def self.to_uri
4
+ RDF::Vocab::LDP.DirectContainer
5
+ end
6
+
7
+ RELATION_TERMS = [RDF::Vocab::LDP.hasMemberRelation,
8
+ RDF::Vocab::LDP.isMemberOfRelation]
9
+
10
+ ##
11
+ # @return [RDF::URI] a URI representing the container type
12
+ def container_class
13
+ CONTAINER_CLASSES[:direct]
14
+ end
15
+
16
+ def add(resource)
17
+ process_membership_resource(resource.to_uri) do |membership, triple|
18
+ super
19
+ membership.graph << triple
20
+ end
21
+ end
22
+
23
+ def remove(resource)
24
+ process_membership_resource(resource.to_uri) do |membership, triple|
25
+ super
26
+ membership.graph.delete(triple)
27
+ end
28
+ end
29
+
30
+ ##
31
+ # Aliases #subject_uri
32
+ # @return [RDF::URI] #subject_uri
33
+ def membership_constant_uri
34
+ case membership_resource_statements.count
35
+ when 0
36
+ graph << RDF::Statement(subject_uri,
37
+ RDF::Vocab::LDP.membershipResource,
38
+ subject_uri)
39
+ subject_uri
40
+ when 1
41
+ membership_resource_statements.first.object
42
+ else
43
+ raise NotAcceptable.new('An LDP-DC MUST have exactly ' \
44
+ 'one membership resource; found ' \
45
+ "#{membership_resource_statements.count}.")
46
+ end
47
+ end
48
+
49
+ ##
50
+ # @return [RDF::URI] the membership predicate
51
+ # @see http://www.w3.org/TR/ldp/#dfn-membership-predicate
52
+ def membership_predicate
53
+ case member_relation_statements.count
54
+ when 0
55
+ graph << RDF::Statement(subject_uri,
56
+ RELATION_TERMS.first,
57
+ RDF::Vocab::LDP.member)
58
+ RDF::Vocab::LDP.member
59
+ when 1
60
+ member_relation_statements.first.object
61
+ else
62
+ raise NotAcceptable.new('An LDP-DC MUST have exactly ' \
63
+ 'one member relation triple; found ' \
64
+ "#{member_relation_statements.count}.")
65
+ end
66
+ end
67
+
68
+ ##
69
+ # @param [RDF::Term] a member for this container
70
+ #
71
+ # @return [RDF::URI] the membership triple to be added to the
72
+ def make_membership_triple(resource)
73
+ predicate = membership_predicate
74
+ return RDF::Statement(membership_constant_uri, predicate, resource) if
75
+ member_relation_statements.first.predicate == RELATION_TERMS.first
76
+ RDF::Statement(resource, predicate, membership_constant_uri)
77
+ end
78
+
79
+ private
80
+
81
+ def membership_resource_statements
82
+ graph.query([subject_uri, RDF::Vocab::LDP.membershipResource, :o])
83
+ end
84
+
85
+ def member_relation_statements
86
+ graph.statements.select do |st|
87
+ st.subject == subject_uri && RELATION_TERMS.include?(st.predicate)
88
+ end
89
+ end
90
+
91
+ def membership_resource
92
+ uri = membership_constant_uri
93
+ uri = uri.fragment ? (uri.root / uri.request_uri) : uri
94
+ RDF::LDP::Resource.find(uri, @data)
95
+ end
96
+
97
+ def process_membership_resource(resource, &block)
98
+ triple = make_membership_triple(resource)
99
+
100
+ begin
101
+ membership_rs = membership_resource
102
+ rescue NotFound => e
103
+ raise NotAcceptable.new('Membership resource ' \
104
+ "#{membership_constant_uri} does not exist")
105
+ end
106
+
107
+ yield(membership_resource, triple, resource) if block_given?
108
+
109
+ self
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,13 @@
1
+ module RDF::LDP
2
+ class IndirectContainer < DirectContainer
3
+ def self.to_uri
4
+ RDF::Vocab::LDP.IndirectContainer
5
+ end
6
+
7
+ ##
8
+ # @return [RDF::URI] a URI representing the container type
9
+ def container_class
10
+ CONTAINER_CLASSES[:indirect]
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module RDF::LDP
2
+ class NonRDFSource < Resource
3
+ ##
4
+ # @return [RDF::URI] uri with lexical representation
5
+ # 'http://www.w3.org/ns/ldp#NonRDFSource'
6
+ #
7
+ # @see http://www.w3.org/TR/ldp/#dfn-linked-data-platform-non-rdf-source
8
+ def self.to_uri
9
+ RDF::Vocab::LDP.NonRDFSource
10
+ end
11
+
12
+ ##
13
+ # @return [Boolean] whether this is an ldp:NonRDFSource
14
+ def non_rdf_source?
15
+ true
16
+ end
17
+ end
18
+ end