ld-patch 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +1 -0
- data/LICENSE +25 -0
- data/README.md +81 -0
- data/VERSION +1 -0
- data/bin/ldpatch +91 -0
- data/lib/ld/patch.rb +79 -0
- data/lib/ld/patch/algebra.rb +24 -0
- data/lib/ld/patch/algebra/add.rb +59 -0
- data/lib/ld/patch/algebra/bind.rb +90 -0
- data/lib/ld/patch/algebra/constraint.rb +56 -0
- data/lib/ld/patch/algebra/cut.rb +56 -0
- data/lib/ld/patch/algebra/delete.rb +59 -0
- data/lib/ld/patch/algebra/index.rb +34 -0
- data/lib/ld/patch/algebra/patch.rb +40 -0
- data/lib/ld/patch/algebra/path.rb +71 -0
- data/lib/ld/patch/algebra/prefix.rb +48 -0
- data/lib/ld/patch/algebra/reverse.rb +39 -0
- data/lib/ld/patch/algebra/update_list.rb +77 -0
- data/lib/ld/patch/meta.rb +2666 -0
- data/lib/ld/patch/parser.rb +621 -0
- data/lib/ld/patch/terminals.rb +91 -0
- data/lib/ld/patch/version.rb +18 -0
- metadata +275 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
module LD::Patch::Algebra
|
2
|
+
|
3
|
+
##
|
4
|
+
# The LD Patch `constraint` operator.
|
5
|
+
#
|
6
|
+
# A constraint is a query operator which either ensures that there is a single input node ("!" operator) or finds a set of nodes for a given `path`, optionally filtering those nodes with a particular predicate value.
|
7
|
+
#
|
8
|
+
# @example existence of path solutions
|
9
|
+
# (constraint (path :p))
|
10
|
+
#
|
11
|
+
# Maps input terms to output terms using `(path :p)` returning those input terms that have at least a single solution.
|
12
|
+
#
|
13
|
+
# @example paths with property value
|
14
|
+
# (constraint (path :p) 1)
|
15
|
+
#
|
16
|
+
# Maps input terms to output terms using `(path :p)` and filters the input terms where the output term is `1`.
|
17
|
+
#
|
18
|
+
# @example unique terms
|
19
|
+
#
|
20
|
+
# (constraint unique)
|
21
|
+
#
|
22
|
+
# Returns the single term from the input terms if there is a single input term.
|
23
|
+
class Constraint < SPARQL::Algebra::Operator
|
24
|
+
include SPARQL::Algebra::Query
|
25
|
+
include SPARQL::Algebra::Evaluatable
|
26
|
+
|
27
|
+
NAME = :constraint
|
28
|
+
|
29
|
+
##
|
30
|
+
# If the first operand is :unique
|
31
|
+
#
|
32
|
+
# @param [RDF::Queryable] queryable
|
33
|
+
# the graph or repository to write
|
34
|
+
# @param [Hash{Symbol => Object}] options
|
35
|
+
# any additional options
|
36
|
+
# @option options [Array<RDF::Term>] starting terms
|
37
|
+
# @return [RDF::Query::Solutions] solutions with `:term` mapping
|
38
|
+
def execute(queryable, options = {})
|
39
|
+
debug(options) {"Constraint"}
|
40
|
+
terms = Array(options.fetch(:terms))
|
41
|
+
op, value = operands
|
42
|
+
|
43
|
+
results = if op == :unique
|
44
|
+
terms.length == 1 ? terms : []
|
45
|
+
else
|
46
|
+
# op is a path, filter input terms based on the presense or absense of output terms. Additionally, if a constraint value is given, output terms must equal that value
|
47
|
+
terms.select do |term|
|
48
|
+
output_terms = op.execute(queryable, options.merge(terms: [term])).map(&:path)
|
49
|
+
output_terms = output_terms.select {|t| t == value} if value
|
50
|
+
!output_terms.empty?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
RDF::Query::Solutions.new(results.map {|t| RDF::Query::Solution.new(path: t)})
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module LD::Patch::Algebra
|
2
|
+
|
3
|
+
##
|
4
|
+
# The LD Patch `cut` operator.
|
5
|
+
#
|
6
|
+
# The Cut operation is recursively remove triples from some starting node.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# (cut ?a)
|
10
|
+
#
|
11
|
+
class Cut < SPARQL::Algebra::Operator::Unary
|
12
|
+
include SPARQL::Algebra::Update
|
13
|
+
include SPARQL::Algebra::Evaluatable
|
14
|
+
|
15
|
+
NAME = :cut
|
16
|
+
|
17
|
+
##
|
18
|
+
# Executes this upate on the given `writable` graph or repository.
|
19
|
+
#
|
20
|
+
# @param [RDF::Queryable] queryable
|
21
|
+
# the graph or repository to write
|
22
|
+
# @param [Hash{Symbol => Object}] options
|
23
|
+
# any additional options
|
24
|
+
# @return [RDF::Query::Solutions] A single solution including passed bindings with `var` bound to the solution.
|
25
|
+
# @raise [IOError]
|
26
|
+
# If no triples are identified, or the operand is an unbound variable or the operand is an unbound variable.
|
27
|
+
# @see http://www.w3.org/TR/sparql11-update/
|
28
|
+
def execute(queryable, options = {})
|
29
|
+
debug(options) {"Cut"}
|
30
|
+
bindings = options.fetch(:bindings)
|
31
|
+
solution = bindings.first
|
32
|
+
var = operand(0)
|
33
|
+
|
34
|
+
# Bind variable
|
35
|
+
raise LD::Patch::Error.new("Operand uses unbound variable #{var.inspect}", code: 400) unless solution.bound?(var)
|
36
|
+
var = solution[var]
|
37
|
+
|
38
|
+
cut_count = 0
|
39
|
+
# Get triples to delete using consice bounded description
|
40
|
+
queryable.concise_bounded_description(var) do |statement|
|
41
|
+
queryable.delete(statement)
|
42
|
+
cut_count += 1
|
43
|
+
end
|
44
|
+
|
45
|
+
# Also delete triples having var in the object position
|
46
|
+
queryable.query(object: var).each do |statement|
|
47
|
+
queryable.delete(statement)
|
48
|
+
cut_count += 1
|
49
|
+
end
|
50
|
+
|
51
|
+
raise LD::Patch::Error, "Cut removed no triples" unless cut_count > 0
|
52
|
+
|
53
|
+
bindings
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module LD::Patch::Algebra
|
2
|
+
|
3
|
+
##
|
4
|
+
# The LD Patch `delete` operator (incuding `deleteExisting`).
|
5
|
+
#
|
6
|
+
# The Add operation is used to delete triples from the target graph with or without checking to see if the exist already.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# (add ((<a> <b> <c>)))
|
10
|
+
#
|
11
|
+
class Delete < SPARQL::Algebra::Operator::Unary
|
12
|
+
include SPARQL::Algebra::Update
|
13
|
+
include SPARQL::Algebra::Evaluatable
|
14
|
+
|
15
|
+
NAME = :delete
|
16
|
+
|
17
|
+
##
|
18
|
+
# Executes this upate on the given `writable` graph or repository.
|
19
|
+
#
|
20
|
+
# @param [RDF::Queryable] queryable
|
21
|
+
# the graph or repository to write
|
22
|
+
# @param [Hash{Symbol => Object}] options
|
23
|
+
# any additional options
|
24
|
+
# @option options [Boolean] :existing
|
25
|
+
# Specifies that triples must already exist in the target graph
|
26
|
+
# @return [RDF::Query::Solutions] A single solution including passed bindings with `var` bound to the solution.
|
27
|
+
# @raise [Error]
|
28
|
+
# If `existing` is specified, and any triple is not found in the traget graph, or if unbound variables are used.
|
29
|
+
# @see http://www.w3.org/TR/sparql11-update/
|
30
|
+
def execute(queryable, options = {})
|
31
|
+
debug(options) {"Delete"}
|
32
|
+
bindings = options.fetch(:bindings)
|
33
|
+
solution = bindings.first
|
34
|
+
|
35
|
+
# Bind variables to triples
|
36
|
+
triples = operand(0).dup.replace_vars! do |var|
|
37
|
+
case var
|
38
|
+
when RDF::Query::Pattern
|
39
|
+
s = var.bind(solution)
|
40
|
+
raise LD::Patch::Error.new("Operand uses unbound pattern #{var.inspect}", code: 400) if s.variable?
|
41
|
+
s
|
42
|
+
when RDF::Query::Variable
|
43
|
+
raise LD::Patch::Error.new("Operand uses unbound variable #{var.inspect}", code: 400) unless solution.bound?(var)
|
44
|
+
solution[var]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# If `:new` is specified, verify that no triple in triples exists in queryable
|
49
|
+
if @options[:existing]
|
50
|
+
triples.each do |triple|
|
51
|
+
raise LD::Patch::Error, "Target graph does not contain triple #{triple.to_ntriples}" unless queryable.has_statement?(triple)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
queryable.delete(*triples)
|
56
|
+
bindings
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module LD::Patch::Algebra
|
2
|
+
|
3
|
+
##
|
4
|
+
# The LD Patch `index` operator.
|
5
|
+
#
|
6
|
+
# Presuming that the input term identifies an rdf:List, returns the list element indexted by the single operand, or an empty solution set
|
7
|
+
class Index < SPARQL::Algebra::Operator::Unary
|
8
|
+
include SPARQL::Algebra::Query
|
9
|
+
|
10
|
+
NAME = :index
|
11
|
+
|
12
|
+
##
|
13
|
+
# Executes this upate on the given `writable` graph or repository.
|
14
|
+
#
|
15
|
+
# @param [RDF::Queryable] queryable
|
16
|
+
# the graph or repository to write
|
17
|
+
# @param [Hash{Symbol => Object}] options
|
18
|
+
# any additional options
|
19
|
+
# @option options [Array<RDF::Term>] starting terms
|
20
|
+
# @return [RDF::Query::Solutions] solutions with `:term` mapping
|
21
|
+
def execute(queryable, options = {})
|
22
|
+
debug(options) {"Index"}
|
23
|
+
terms = Array(options.fetch(:terms))
|
24
|
+
index = operand(0)
|
25
|
+
|
26
|
+
results = terms.map do |term|
|
27
|
+
list = RDF::List.new(term, queryable)
|
28
|
+
list.at(index.to_i)
|
29
|
+
end.flatten
|
30
|
+
|
31
|
+
RDF::Query::Solutions.new(results.map {|t| RDF::Query::Solution.new(path: t)})
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module LD::Patch::Algebra
|
2
|
+
|
3
|
+
##
|
4
|
+
# The LD Patch `patch` operator.
|
5
|
+
#
|
6
|
+
# Transactionally iterates over all operands building bindings along the way
|
7
|
+
class Patch < SPARQL::Algebra::Operator
|
8
|
+
include SPARQL::Algebra::Update
|
9
|
+
|
10
|
+
NAME = :patch
|
11
|
+
|
12
|
+
##
|
13
|
+
# Executes this upate on the given `writable` graph or repository.
|
14
|
+
#
|
15
|
+
# @param [RDF::Queryable] queryable
|
16
|
+
# the graph or repository to write
|
17
|
+
# @param [Hash{Symbol => Object}] options
|
18
|
+
# any additional options
|
19
|
+
# @return [RDF::Queryable]
|
20
|
+
# Returns queryable.
|
21
|
+
# @raise [Error]
|
22
|
+
# If any error is caught along the way, and rolls back the transaction
|
23
|
+
def execute(queryable, options = {})
|
24
|
+
debug(options) {"Delete"}
|
25
|
+
|
26
|
+
# FIXME: due to insufficient transaction support, this is implemented by running through operands twice: the first using a clone of the graph, and the second acting on the graph directly
|
27
|
+
graph = RDF::Graph.new << queryable
|
28
|
+
loop do
|
29
|
+
operands.inject(RDF::Query::Solutions.new([RDF::Query::Solution.new])) do |bindings, op|
|
30
|
+
# Invoke operand using bindings from prvious operation
|
31
|
+
op.execute(graph, options.merge(bindings: bindings))
|
32
|
+
end
|
33
|
+
|
34
|
+
break if graph.equal?(queryable)
|
35
|
+
graph = queryable
|
36
|
+
end
|
37
|
+
queryable
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module LD::Patch::Algebra
|
2
|
+
|
3
|
+
##
|
4
|
+
# The LD Patch `path` operator.
|
5
|
+
#
|
6
|
+
# The Path creates a closure over path operands querying `queryable` for terms having a relationship with the input `terms` based on each operand. The terms extracted from the first operand are used as inputs for the next operand until a final set of terms is found. These terms are returned as `RDF:Query::Solution` bound the the variable `?path`
|
7
|
+
#
|
8
|
+
# @example empty path
|
9
|
+
# (path)
|
10
|
+
#
|
11
|
+
# Returns input terms
|
12
|
+
#
|
13
|
+
# @example forward path
|
14
|
+
# (path :p)
|
15
|
+
#
|
16
|
+
# Queries `queryable` for objects where the input terms are subjects and the predicate is `:p`
|
17
|
+
#
|
18
|
+
# @example reverse path
|
19
|
+
# (path (reverse :p))
|
20
|
+
#
|
21
|
+
# Queries `queryable` for subjects where input terms are objects and the predicate is `:p`, by executing the `reverse` operand using input terms to get a set of output terms.
|
22
|
+
#
|
23
|
+
# @example constraint
|
24
|
+
# (path (constraint (path) :c, 1))
|
25
|
+
#
|
26
|
+
# Returns the input terms satisfying the constrant.
|
27
|
+
#
|
28
|
+
# @example chained path elements
|
29
|
+
# (path :p :q (constraint (path) :c, 1))
|
30
|
+
#
|
31
|
+
# Maps terms using `(path :p)`, using them as terms for `(path :q)`, then subsets these based on the constraint.
|
32
|
+
class Path < SPARQL::Algebra::Operator
|
33
|
+
include SPARQL::Algebra::Query
|
34
|
+
include SPARQL::Algebra::Evaluatable
|
35
|
+
|
36
|
+
NAME = :path
|
37
|
+
|
38
|
+
##
|
39
|
+
# Executes this operator using the given variable `bindings` and a starting term, returning zero or more terms at the end of the path.
|
40
|
+
#
|
41
|
+
# @param [RDF::Queryable] queryable
|
42
|
+
# the graph or repository to query
|
43
|
+
# @param [Hash{Symbol => Object}] options ({})
|
44
|
+
# options passed from query
|
45
|
+
# @option options [Array<RDF::Term>] starting terms
|
46
|
+
# @return [RDF::Query::Solutions] solutions with `:term` mapping
|
47
|
+
def execute(queryable, options = {})
|
48
|
+
solutions = RDF::Query::Solutions.new
|
49
|
+
|
50
|
+
# Iterate updating terms, then create solutions from matched terms
|
51
|
+
operands.inject(Array(options.fetch(:terms))) do |terms, op|
|
52
|
+
case op
|
53
|
+
when RDF::URI
|
54
|
+
terms.map do |subject|
|
55
|
+
queryable.query(subject: subject, predicate: op).map(&:object)
|
56
|
+
end.flatten
|
57
|
+
when SPARQL::Algebra::Query
|
58
|
+
# Get path solutions for each term for op
|
59
|
+
op.execute(queryable, options.merge(terms: terms)).map do |soln|
|
60
|
+
soln.path
|
61
|
+
end.flatten
|
62
|
+
else
|
63
|
+
raise NotImplementedError, "Unknown path operand #{op.inspect}"
|
64
|
+
end
|
65
|
+
end.each do |term|
|
66
|
+
solutions << RDF::Query::Solution.new(path: term)
|
67
|
+
end
|
68
|
+
solutions
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module LD::Patch::Algebra
|
2
|
+
##
|
3
|
+
# The LD Patch `prefix` operator.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# (prefix ((: <http://example/>))
|
7
|
+
# (graph ?g
|
8
|
+
# (bgp (triple ?s ?p ?o))))
|
9
|
+
#
|
10
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#QSynIRI
|
11
|
+
class Prefix < SPARQL::Algebra::Operator::Binary
|
12
|
+
include SPARQL::Algebra::Update
|
13
|
+
|
14
|
+
NAME = :prefix
|
15
|
+
|
16
|
+
##
|
17
|
+
# Executes this query on the given `queryable` graph or repository.
|
18
|
+
# Really a pass-through, as this is a syntactic object used for providing
|
19
|
+
# context for URIs.
|
20
|
+
#
|
21
|
+
# @param [RDF::Queryable] queryable
|
22
|
+
# the graph or repository to query
|
23
|
+
# @param [Hash{Symbol => Object}] options
|
24
|
+
# any additional keyword options
|
25
|
+
# @yield [solution]
|
26
|
+
# each matching solution, statement or boolean
|
27
|
+
# @yieldparam [RDF::Statement, RDF::Query::Solution, Boolean] solution
|
28
|
+
# @yieldreturn [void] ignored
|
29
|
+
# @return [RDF::Query::Solutions]
|
30
|
+
# the resulting solution sequence
|
31
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
|
32
|
+
def execute(queryable, options = {}, &block)
|
33
|
+
debug(options) {"Prefix"}
|
34
|
+
@solutions = queryable.query(operands.last, options.merge(depth: options[:depth].to_i + 1), &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Returns an optimized version of this query.
|
39
|
+
#
|
40
|
+
# If optimize operands, and if the first two operands are both Queries, replace
|
41
|
+
# with the unique sum of the query elements
|
42
|
+
#
|
43
|
+
# @return [Union, RDF::Query] `self`
|
44
|
+
def optimize
|
45
|
+
operands.last.optimize
|
46
|
+
end
|
47
|
+
end # Prefix
|
48
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module LD::Patch::Algebra
|
2
|
+
|
3
|
+
##
|
4
|
+
# The LD Patch `reverse` operator
|
5
|
+
#
|
6
|
+
# Finds all the terms which are the subject of triples where the `operand` is the predicate and input terms are objects.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# (reverse :p)
|
10
|
+
#
|
11
|
+
# Queries `queryable` for subjects where input terms are objects and the predicate is `:p`, by executing the `reverse` operand using input terms to get a set of output terms.
|
12
|
+
class Reverse < SPARQL::Algebra::Operator::Unary
|
13
|
+
include SPARQL::Algebra::Query
|
14
|
+
include SPARQL::Algebra::Evaluatable
|
15
|
+
|
16
|
+
NAME = :reverse
|
17
|
+
|
18
|
+
##
|
19
|
+
# Executes this upate on the given `writable` graph or repository.
|
20
|
+
#
|
21
|
+
# @param [RDF::Queryable] queryable
|
22
|
+
# the graph or repository to write
|
23
|
+
# @param [Hash{Symbol => Object}] options
|
24
|
+
# any additional options
|
25
|
+
# @option options [Array<RDF::Term>] starting terms
|
26
|
+
# @return [RDF::Query::Solutions] solutions with `:term` mapping
|
27
|
+
def execute(queryable, options = {})
|
28
|
+
debug(options) {"Reverse"}
|
29
|
+
op = operand(0)
|
30
|
+
terms = Array(options.fetch(:terms))
|
31
|
+
|
32
|
+
results = terms.map do |object|
|
33
|
+
queryable.query(object: object, predicate: op).map(&:subject)
|
34
|
+
end.flatten
|
35
|
+
|
36
|
+
RDF::Query::Solutions.new(results.map {|t| RDF::Query::Solution.new(path: t)})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module LD::Patch::Algebra
|
2
|
+
|
3
|
+
##
|
4
|
+
# The LD Patch `updateList` operator.
|
5
|
+
#
|
6
|
+
# The UpdateList operation is used to splice a new list into a subset of an existing list.
|
7
|
+
#
|
8
|
+
class UpdateList < SPARQL::Algebra::Operator
|
9
|
+
include SPARQL::Algebra::Update
|
10
|
+
include SPARQL::Algebra::Evaluatable
|
11
|
+
|
12
|
+
NAME = :updateList
|
13
|
+
|
14
|
+
##
|
15
|
+
# Executes this upate on the given `writable` graph or repository.
|
16
|
+
#
|
17
|
+
# @param [RDF::Queryable] queryable
|
18
|
+
# the graph or repository to write
|
19
|
+
# @param [Hash{Symbol => Object}] options
|
20
|
+
# any additional options
|
21
|
+
# @return [RDF::Query::Solutions] A single solution including passed bindings with `var` bound to the solution.
|
22
|
+
# @raise [Error]
|
23
|
+
# If the subject and predicate provided to an UpdateList do not have a unique object, or if this object is not a well-formed collection.
|
24
|
+
# If an index in a slice expression is greater than the length of the rdf:List or otherwise out of bound.
|
25
|
+
# @see http://www.w3.org/TR/sparql11-update/
|
26
|
+
def execute(queryable, options = {})
|
27
|
+
debug(options) {"UpdateList"}
|
28
|
+
bindings = options.fetch(:bindings)
|
29
|
+
solution = bindings.first
|
30
|
+
var_or_iri, predicate, slice1, slice2, collection = operands
|
31
|
+
|
32
|
+
# Bind variables to path
|
33
|
+
if var_or_iri.variable?
|
34
|
+
raise LD::Patch::Error("Operand uses unbound variable #{var_or_iri.inspect}", code: 400) unless solution.bound?(var_or_iri)
|
35
|
+
var_or_iri = solution[variable]
|
36
|
+
end
|
37
|
+
|
38
|
+
list_heads = queryable.query(subject: var_or_iri, predicate: predicate).map {|s| s.object}
|
39
|
+
|
40
|
+
raise LD::Patch::Error, "UpdateList ambigious value for #{var_or_iri.to_ntriples} and #{predicate.to_ntriples}" if list_heads.length > 1
|
41
|
+
raise LD::Patch::Error, "UpdateList no value found for #{var_or_iri.to_ntriples} and #{predicate.to_ntriples}" if list_heads.empty?
|
42
|
+
lh = list_heads.first
|
43
|
+
list = RDF::List.new(lh, queryable)
|
44
|
+
raise LD::Patch::Error, "Invalid list" unless list.valid?
|
45
|
+
|
46
|
+
start = case
|
47
|
+
when slice1.nil? || slice1 == RDF.nil then list.length
|
48
|
+
when slice1 < 0 then list.length + slice1.to_i
|
49
|
+
else slice1.to_i
|
50
|
+
end
|
51
|
+
|
52
|
+
finish = case
|
53
|
+
when slice2.nil? || slice2 == RDF.nil then list.length
|
54
|
+
when slice2 < 0 then list.length + slice2.to_i
|
55
|
+
else slice2.to_i
|
56
|
+
end
|
57
|
+
|
58
|
+
raise LD::Patch::Error.new("UpdateList slice indexes out of order #{start}..#{finish}}", code: 400) if finish < start
|
59
|
+
|
60
|
+
length = finish - start
|
61
|
+
raise LD::Patch::Error, "UpdateList out of bounds #{start}..#{finish}}" if start + length > list.length
|
62
|
+
raise LD::Patch::Error, "UpdateList out of bounds #{start}..#{finish}}" if start < 0
|
63
|
+
|
64
|
+
# Uses #[]= logic in RDF::List
|
65
|
+
list[start, length] = collection
|
66
|
+
new_lh = list.subject
|
67
|
+
|
68
|
+
# If lh was rdf:nil, then we may have a new list head. Similarly, if the list was emptied, we now need to replace the head
|
69
|
+
if lh != new_lh
|
70
|
+
queryable.delete(RDF::Statement(var_or_iri, predicate, lh))
|
71
|
+
queryable.insert(RDF::Statement(var_or_iri, predicate, new_lh))
|
72
|
+
end
|
73
|
+
|
74
|
+
bindings
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|