Class: RDF::LDP::Resource
- Inherits:
-
Object
- Object
- RDF::LDP::Resource
- Defined in:
- lib/rdf/ldp/resource.rb
Overview
The base class for all LDP Resources.
The internal state of a Resource is specific to a given persistent datastore (an ‘RDF::Repository` passed to the initilazer) and is managed through an internal graph (`#metagraph`). A Resource has:
- a `#subject_uri` identifying the Resource.
- a `#metagraph` containing server-internal properties of the Resource.
Resources also define a basic set of CRUD operations, identity and current state, and a ‘#to_response`/`#each` method used by Rack & `Rack::LDP` to generate an appropriate HTTP response body.
‘#metagraph’ holds internal properites used by the server. It is distinct from, and may conflict with, other RDF and non-RDF information about the resource (e.g. representations suitable for a response body). Metagraph contains a canonical ‘rdf:type` statement, which specifies the resource’s interaction model and a (dcterms:modified) last-modified date. If the resource is deleted, a (prov:invalidatedAt) flag in metagraph indicates this.
The contents of ‘#metagraph` should not be confused with LDP server-managed-triples, Those triples are included in the state of the resource as represented by the response body. `#metagraph` is invisible to the client except where a subclass mirrors its contents in the body.
Rack (via ‘RDF::LDP::Rack`) uses the `#request` method to dispatch requests and interpret responses. Disallowed HTTP methods result in `RDF::LDP::MethodNotAllowed`. Individual Resources populate `Link`, `Allow`, `ETag`, `Last-Modified`, and `Accept-*` headers as required by LDP. All subclasses (MUST) return `self` as the Body, and respond to `#each`/ `#respond_to` with the intended body.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#metagraph ⇒ Object
a graph representing the server-internal state of the resource.
-
#subject_uri ⇒ Object
readonly
Returns the value of attribute subject_uri.
Class Method Summary collapse
-
.find(uri, data) ⇒ RDF::LDP::Resource
Finds an existing resource and.
-
.gen_id ⇒ String
Creates an unique id (URI Slug) for a resource.
-
.interaction_model(link_header) ⇒ Class
Retrieves the correct interaction model from the Link headers.
-
.metagraph_name(uri) ⇒ Object
Build a graph name URI for the uri passed in.
-
.to_uri ⇒ RDF::URI
Uri with lexical representation ‘www.w3.org/ns/ldp#Resource’.
Instance Method Summary collapse
-
#allowed_methods ⇒ Array<Symbol>
A list of HTTP methods allowed by this resource.
-
#container? ⇒ Boolean
Whether this is an ldp:Container.
-
#containers ⇒ Array<RDF::LDP::Resource>
The container for this resource.
-
#create(input, content_type) {|tx| ... } ⇒ RDF::LDP::Resource
abstract
Self.
-
#destroy {|tx| ... } ⇒ RDF::LDP::Resource
Mark the resource as destroyed.
-
#destroyed? ⇒ Boolean
True if resource has been destroyed.
-
#etag ⇒ String
Returns an Etag.
-
#exists? ⇒ Boolean
Gives the status of the resource’s existance.
-
#initialize(subject_uri, data = RDF::Repository.new) {|RDF::Resource| ... } ⇒ Resource
constructor
A new instance of Resource.
-
#last_modified ⇒ DateTime
The time this resource was last modified; ‘nil` if the resource doesn’t exist and has no modified date.
-
#ldp_resource? ⇒ Boolean
Whether this is an ldp:Resource.
-
#match?(tag) ⇒ Boolean
Whether the given tag matches ‘#etag`.
-
#non_rdf_source? ⇒ Boolean
Whether this is an ldp:NonRDFSource.
-
#rdf_source? ⇒ Boolean
Whether this is an ldp:RDFSource.
-
#request(method, status, headers, env) ⇒ Array<Fixnum, Hash<String, String>, #each] a new Rack response array.
Build the response for the HTTP ‘method` given.
-
#to_response ⇒ Object
(also: #each)
Runs the request and returns the object’s desired HTTP response body, conforming to the Rack interfare.
-
#to_uri ⇒ RDF::URI
The subject URI for this resource.
-
#update(input, content_type) {|tx| ... } ⇒ RDF::LDP::Resource
abstract
Self.
Constructor Details
#initialize(subject_uri, data = RDF::Repository.new) {|RDF::Resource| ... } ⇒ Resource
Returns a new instance of Resource.
196 197 198 199 200 201 |
# File 'lib/rdf/ldp/resource.rb', line 196 def initialize(subject_uri, data = RDF::Repository.new) @subject_uri = RDF::URI(subject_uri) @data = data @metagraph = RDF::Graph.new(, data: data) yield self if block_given? end |
Instance Attribute Details
#metagraph ⇒ Object
a graph representing the server-internal state of the resource
93 94 95 |
# File 'lib/rdf/ldp/resource.rb', line 93 def @metagraph end |
#subject_uri ⇒ Object (readonly)
Returns the value of attribute subject_uri.
89 90 91 |
# File 'lib/rdf/ldp/resource.rb', line 89 def subject_uri @subject_uri end |
Class Method Details
.find(uri, data) ⇒ RDF::LDP::Resource
Finds an existing resource and
127 128 129 130 131 132 133 134 135 136 |
# File 'lib/rdf/ldp/resource.rb', line 127 def find(uri, data) graph = RDF::Graph.new((uri), data: data) raise NotFound if graph.empty? rdf_class = graph.query([uri, RDF.type, :o]).first klass = INTERACTION_MODELS[rdf_class.object] if rdf_class klass ||= RDFSource klass.new(uri, data) end |
.gen_id ⇒ String
the current implementation uses SecureRandom#uuid.
Creates an unique id (URI Slug) for a resource.
111 112 113 |
# File 'lib/rdf/ldp/resource.rb', line 111 def gen_id SecureRandom.uuid end |
.interaction_model(link_header) ⇒ Class
Retrieves the correct interaction model from the Link headers.
Headers are handled intelligently, e.g. if a client sends a request with Resource, RDFSource, and BasicContainer headers, the server gives a BasicContainer. An error is thrown if the headers contain conflicting types (i.e. NonRDFSource and another Resource class).
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/rdf/ldp/resource.rb', line 151 def interaction_model(link_header) models = LinkHeader.parse(link_header) .links.select { |link| link['rel'].downcase == 'type' } .map { |link| link.href } return RDFSource if models.empty? match = INTERACTION_MODELS.keys.reverse.find { |u| models.include? u } if match == RDF::LDP::NonRDFSource.to_uri raise NotAcceptable if models.include?(RDF::LDP::RDFSource.to_uri) || models.include?(RDF::LDP::Container.to_uri) || models.include?(RDF::LDP::DirectContainer.to_uri) || models.include?(RDF::LDP::IndirectContainer.to_uri) || models.include?(RDF::URI('http://www.w3.org/ns/ldp#BasicContainer')) end INTERACTION_MODELS[match] || RDFSource end |
.metagraph_name(uri) ⇒ Object
Build a graph name URI for the uri passed in
175 176 177 |
# File 'lib/rdf/ldp/resource.rb', line 175 def (uri) uri + '#meta' end |
.to_uri ⇒ RDF::URI
Returns uri with lexical representation ‘www.w3.org/ns/ldp#Resource’.
101 102 103 |
# File 'lib/rdf/ldp/resource.rb', line 101 def to_uri RDF::Vocab::LDP.Resource end |
Instance Method Details
#allowed_methods ⇒ Array<Symbol>
Returns a list of HTTP methods allowed by this resource.
350 351 352 353 354 |
# File 'lib/rdf/ldp/resource.rb', line 350 def allowed_methods [:GET, :POST, :PUT, :DELETE, :PATCH, :OPTIONS, :HEAD].select do |m| respond_to?(m.downcase, true) end end |
#container? ⇒ Boolean
Returns whether this is an ldp:Container.
364 365 366 |
# File 'lib/rdf/ldp/resource.rb', line 364 def container? false end |
#containers ⇒ Array<RDF::LDP::Resource>
Returns the container for this resource.
382 383 384 385 386 |
# File 'lib/rdf/ldp/resource.rb', line 382 def containers @data.query([:s, RDF::Vocab::LDP.contains, subject_uri]).map do |st| RDF::LDP::Resource.find(st.subject, @data) end end |
#create(input, content_type) {|tx| ... } ⇒ RDF::LDP::Resource
creates the resource
Returns self.
220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/rdf/ldp/resource.rb', line 220 def create(input, content_type, &block) raise Conflict if exists? @data.transaction do |transaction| set_interaction_model(transaction) yield transaction if block_given? set_last_modified(transaction) end self end |
#destroy {|tx| ... } ⇒ RDF::LDP::Resource
Use of owl:Nothing is probably problematic. Define an internal
Mark the resource as destroyed.
This adds a statment to the metagraph expressing that the resource has been deleted
namespace and class represeting deletion status as a stateful property.
269 270 271 272 273 274 275 276 277 278 279 |
# File 'lib/rdf/ldp/resource.rb', line 269 def destroy(&block) @data.transaction do |transaction| containers.each { |c| c.remove(self, transaction) if c.container? } transaction << RDF::Statement(subject_uri, RDF::Vocab::PROV.invalidatedAtTime, DateTime.now, graph_name: ) yield if block_given? end self end |
#destroyed? ⇒ Boolean
Returns true if resource has been destroyed.
294 295 296 297 |
# File 'lib/rdf/ldp/resource.rb', line 294 def destroyed? times = @metagraph.query([subject_uri, RDF::Vocab::PROV.invalidatedAtTime, nil]) !(times.empty?) end |
#etag ⇒ String
these etags are strong if (and only if) all software that updates the resource also updates the ETag
Returns an Etag. This may be a strong or a weak ETag.
311 312 313 314 |
# File 'lib/rdf/ldp/resource.rb', line 311 def etag return nil unless exists? "W/\"#{last_modified.new_offset(0).iso8601(9)}\"" end |
#exists? ⇒ Boolean
destroyed resources continue to exist in the sense represeted by this method.
Gives the status of the resource’s existance.
288 289 290 |
# File 'lib/rdf/ldp/resource.rb', line 288 def exists? @data.has_graph? .graph_name end |
#last_modified ⇒ DateTime
handle cases where there is more than one RDF::DC.modified. check for the most recent date
Returns the time this resource was last modified; ‘nil` if the resource doesn’t exist and has no modified date.
324 325 326 327 328 329 330 331 332 333 |
# File 'lib/rdf/ldp/resource.rb', line 324 def last_modified results = @metagraph.query([subject_uri, RDF::Vocab::DC.modified, :time]) if results.empty? return nil unless exists? raise(RequestError, "Missing dc:modified date for #{subject_uri}") end results.first.object.object end |
#ldp_resource? ⇒ Boolean
Returns whether this is an ldp:Resource.
358 359 360 |
# File 'lib/rdf/ldp/resource.rb', line 358 def ldp_resource? true end |
#match?(tag) ⇒ Boolean
Returns whether the given tag matches ‘#etag`.
338 339 340 |
# File 'lib/rdf/ldp/resource.rb', line 338 def match?(tag) tag == etag end |
#non_rdf_source? ⇒ Boolean
Returns whether this is an ldp:NonRDFSource.
370 371 372 |
# File 'lib/rdf/ldp/resource.rb', line 370 def non_rdf_source? false end |
#rdf_source? ⇒ Boolean
Returns whether this is an ldp:RDFSource.
376 377 378 |
# File 'lib/rdf/ldp/resource.rb', line 376 def rdf_source? false end |
#request(method, status, headers, env) ⇒ Array<Fixnum, Hash<String, String>, #each] a new Rack response array.
Build the response for the HTTP ‘method` given.
The method passed in is symbolized, downcased, and sent to ‘self` with the other three parameters.
Request methods are expected to return an Array appropriate for a Rack response; to return this object (e.g. for a sucessful GET) the response may be ‘[status, headers, self]`.
If the method given is unimplemented, we understand it to require an HTTP 405 response, and throw the appropriate error.
423 424 425 426 427 428 429 430 |
# File 'lib/rdf/ldp/resource.rb', line 423 def request(method, status, headers, env) raise Gone if destroyed? begin send(method.to_sym.downcase, status, headers, env) rescue NotImplementedError => e raise MethodNotAllowed, method end end |
#to_response ⇒ Object Also known as: each
Runs the request and returns the object’s desired HTTP response body, conforming to the Rack interfare.
394 395 396 |
# File 'lib/rdf/ldp/resource.rb', line 394 def to_response [] end |
#to_uri ⇒ RDF::URI
Returns the subject URI for this resource.
344 345 346 |
# File 'lib/rdf/ldp/resource.rb', line 344 def to_uri subject_uri end |
#update(input, content_type) {|tx| ... } ⇒ RDF::LDP::Resource
update the resource
Returns self.
247 248 249 250 251 252 253 254 |
# File 'lib/rdf/ldp/resource.rb', line 247 def update(input, content_type, &block) return create(input, content_type, &block) unless exists? @data.transaction do |transaction| yield transaction if block_given? set_last_modified(transaction) end self end |