lib/rdf/ldp/container.rb in rdf-ldp-0.3.0 vs lib/rdf/ldp/container.rb in rdf-ldp-0.4.0
- old
+ new
@@ -32,39 +32,67 @@
def container_class
CONTAINER_CLASSES[:basic]
end
##
+ # Create with validation as required for the LDP container.
+ #
+ # @raise [RDF::LDP::Conflict] if the create inserts triples that are not
+ # allowed by LDP for the container type
# @see RDFSource#create
- def create(input, content_type)
- super { |statements| validate_triples!(statements) }
+ def create(input, content_type, &block)
+ super do |transaction|
+ validate_triples!(transaction)
+ yield transaction if block_given?
+ end
+ self
end
##
+ # Updates with validation as required for the LDP container.
+ #
+ # @raise [RDF::LDP::Conflict] if the update edits triples that are not
+ # allowed by LDP for the container type
# @see RDFSource#update
- def update(input, content_type)
- super { |statements| validate_triples!(statements) }
+ def update(input, content_type, &block)
+ super do |transaction|
+ validate_triples!(transaction)
+ yield transaction if block_given?
+ end
+ self
end
##
# Adds a member `resource` to the container. Handles containment and
# membership triples as appropriate for the container type.
#
+ # If a transaction is passed as the second argument, the additon of the
+ # containment triple is completed when the transaction closes; otherwise it is
+ # handled atomically.
+ #
# @param [RDF::Term] a new member for this container
+ # @param transaction [RDF::Transaction] an active transaction as context for
+ # the addition
# @return [Container] self
- def add(resource)
- add_containment_triple(resource.to_uri)
+ def add(resource, transaction = nil)
+ add_containment_triple(resource.to_uri, transaction)
end
##
# Removes a member `resource` from the container. Handles containment and
# membership triples as appropriate for the container type.
#
+ # If a transaction is passed as the second argument, the removal of the
+ # containment triple is completed when the transaction closes; otherwise it is
+ # handled atomically.
+ #
# @param [RDF::Term] a new member for this container
+ # @param transaction [RDF::Transaction] an active transaction as context for
+ # the removal
# @return [Container] self
- def remove(resource)
- remove_containment_triple(resource.to_uri)
+ def remove(resource, transaction = nil)
+ remove_containment_triple(resource.to_uri, transaction)
end
##
# @return [RDF::Query::Enumerator] the containment triples
def containment_triples
@@ -75,43 +103,54 @@
##
# @param [RDF::Statement] statement
#
# @return [Boolean] true if the containment triple exists
- #
- # @todo for some reason `#include?` doesn't work! figure out why, this is
- # clumsy.
def has_containment_triple?(statement)
- !(containment_triples.select { |t| statement == t }.empty?)
+ !(containment_triples.find { |t| statement == t }.nil?)
end
##
# Adds a containment triple for `resource` to the container's `#graph`.
#
- # @param [RDF::Term] a new member for this container
+ # If a transaction is passed as the second argument, the triple is added to
+ # the transaction's inserts; otherwise it is added directly to `#graph`.
+ #
+ # @param resource [RDF::Term] a new member for this container
+ # @param transaction [RDF::Transaction]
# @return [Container] self
- def add_containment_triple(resource)
- graph << make_containment_triple(resource)
+ def add_containment_triple(resource, transaction = nil)
+ target = transaction || graph
+ target << make_containment_triple(resource)
+ set_last_modified(transaction)
self
end
##
# Remove a containment triple for `resource` to the container's `#graph`.
#
- # @param [RDF::Term] a member to remove from this container
+ # If a transaction is passed as the second argument, the triple is added to
+ # the transaction's deletes; otherwise it is deleted directly from `#graph`.
+ #
+ # @param resource [RDF::Term] a member to remove from this container
+ # @param transaction [RDF::Transaction]
# @return [Container] self
- def remove_containment_triple(resource)
- graph.delete(make_containment_triple(resource))
+ def remove_containment_triple(resource, transaction = nil)
+ target = transaction || graph
+ target.delete(make_containment_triple(resource))
+ set_last_modified(transaction)
self
end
##
- # @param [RDF::Term] a member for this container
+ # @param [RDF::Term] a member to be represented in the containment triple
#
- # @return [RDF::URI] the containment triple
+ # @return [RDF::URI] the containment triple, with a graph_name pointing
+ # to `#graph`
def make_containment_triple(resource)
- RDF::Statement(subject_uri, RDF::Vocab::LDP.contains, resource)
+ RDF::Statement(subject_uri, RDF::Vocab::LDP.contains, resource,
+ graph_name: subject_uri)
end
private
def patch(status, headers, env)
@@ -121,14 +160,15 @@
raise UnsupportedMediaType unless method
temp_graph = RDF::Graph.new << graph.statements
send(method, env['rack.input'], temp_graph)
- validate_triples!(temp_graph)
+ validate_statements!(temp_graph)
graph.clear!
graph << temp_graph.statements
+ set_last_modified
[200, update_headers(headers), self]
end
##
# Handles a POST request. Parses a graph in the body of `env` and treats all
@@ -147,17 +187,48 @@
slug.include? '#'
id = (subject_uri / slug).canonicalize
created = klass.new(id, @data)
- .create(env['rack.input'], env['CONTENT_TYPE'])
+
+ created.create(env['rack.input'], env['CONTENT_TYPE']) do |transaction|
+ add(created, transaction)
+ end
- add(created)
headers['Location'] = created.subject_uri.to_s
[201, created.send(:update_headers, headers), created]
end
- def validate_triples!(statements)
+ def validate_triples!(transaction)
+ existing_triples = containment_triples.to_a
+
+ inserts = transaction.inserts.select do |st|
+ st.subject == subject_uri && st.predicate == RDF::Vocab::LDP.contains
+ end
+
+ inserts.each do |statement|
+ existing_triples.delete(statement) do
+ raise Conflict.new('Attempted to write unacceptable LDP ' \
+ "containment-triple: #{statement}")
+ end
+ end
+
+ deletes = transaction.deletes.select do |st|
+ st.subject == subject_uri &&
+ predicate == RDF::Vocab::LDP.contains &&
+ !inserts.include?(st)
+ end
+
+ deletes = deletes + existing_triples
+
+ raise Conflict.new('Cannot remove containment triples in updates. ' \
+ "Attepted to remove #{deletes}") unless
+ deletes.empty?
+ end
+
+ ##
+ # supports Patch.
+ def validate_statements!(statements)
existing_triples = containment_triples.to_a
statements.query(subject: subject_uri,
predicate: RDF::Vocab::LDP.contains) do |statement|
existing_triples.delete(statement) do
raise Conflict.new('Attempted to write unacceptable LDP ' \