ld-patch 0.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 +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
|