Skip to content

Commit

Permalink
Make Relation#delete singular; add #subtract
Browse files Browse the repository at this point in the history
This makes the interface closer to that of `Set`.
  • Loading branch information
Tom Johnson committed Apr 6, 2016
1 parent 65251e6 commit 16b0b96
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 34 deletions.
50 changes: 34 additions & 16 deletions lib/active_triples/relation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,17 @@ def build(attributes={})
end

##
# @note this method behaves somewhat differently from `Array#delete`. It
# accepts multiple values to delete via a splat argument, and will delete
# any of the values preseent. It always returns `self` unless an error
# is raised.
# @note this method behaves somewhat differently from `Array#delete`.
# It succeeds on deletion of non-existing values, always returning
# `self` unless an error is raised. There is no option to pass a block
# to evaluate if the value is not present. This is because access for
# `value` depends on query time. i.e. the `Relation` set does not have an
# underlying efficient data structure allowing a reliably cheap existence
# check.
#
# @note symbols are treated as RDF::Nodes by default in
# `RDF::Mutable#delete`, but may also represent tokens in statements.
# This casts sybbols to a literals, which gets us symmetric behavior
# This casts symbols to a literals, which gets us symmetric behavior
# between `#set(:sym)` and `#delete(:sym)`.
#
# @example deleting a value
Expand All @@ -211,24 +214,39 @@ def build(attributes={})
# resource.title.delete('moomin') # => ["valley"]
# resource.title # => ['valley']
#
# @example deleting multiple values
# resource = MySource.new
# resource.title = ['moomin', 'valley']
# resource.title.delete('moomin', 'valley')
# resource.title # => []
#
# @example note the behavior of unmatched values
# resource = MySource.new
# resource.title = 'moomin'
# resource.title.delete('valley') # => ["moomin"]
# resource.title # => ['moomin']
#
# @param *values [Array<Object>]
# @param values [Object]
# @return [ActiveTriples::Relation] self
def delete(*values)
statements = values.map do |val|
val = RDF::Literal(val) if val.is_a? Symbol
[rdf_subject, predicate, val]
def delete(value)
value = RDF::Literal(value) if value.is_a? Symbol
parent.delete([rdf_subject, predicate, value])

self
end

##
# @overload subtract(enum)
# Deletes objects in the enumerable from the relation
# @param values [Enumerable] an enumerable of objects to delete
# @overload subtract(*values)
# Deletes each argument value from the relation
# @param *values [Array<Object>] the objects to delete
#
# @return [Relation] self
#
# @note This casts symbols to a literals, which gets us symmetric behavior
# with `#set(:sym)`.
# @see #delete
def subtract(*values)
values = values.first if values.first.is_a? Enumerable
statements = values.map do |value|
value = RDF::Literal(value) if value.is_a? Symbol
[rdf_subject, predicate, value]
end

parent.delete(*statements)
Expand Down
43 changes: 25 additions & 18 deletions spec/active_triples/relation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,20 +88,13 @@ class WithTitle

describe '#delete' do
include_context 'with symbol property'

let(:parent_resource) { ActiveTriples::Resource.new }

it 'can delete nothing' do
expect { subject.delete }.not_to change { subject.to_a }
end

it 'handles a non-existent value' do
expect { subject.delete(1) }.not_to change { subject.to_a }
end

it 'handles multiple non-existent values' do
expect { subject.delete(1, 'blah') }.not_to change { subject.to_a }
end

context 'with values' do
before { subject << values }

Expand All @@ -113,22 +106,12 @@ class WithTitle
expect { subject.delete('blah') }.not_to change { subject.to_a }
end

it 'handles multiple non-existent values' do
expect { subject.delete('blah', 2) }.not_to change { subject.to_a }
end

it 'deletes a matched value' do
expect { subject.delete(values.first) }
.to change { subject.to_a }
.to contain_exactly(*values[1..-1])
end

it 'deletes multiple matched values' do
expect { subject.delete(values.first, values.last) }
.to change { subject.to_a }
.to contain_exactly(*values[1..-2])
end

it 'deletes a URI value' do
values.delete(uri)
expect { subject.delete(uri) }
Expand All @@ -152,6 +135,30 @@ class WithTitle
end
end

describe '#subtract' do
include_context 'with symbol property'

let(:parent_resource) { ActiveTriples::Resource.new }

it 'subtracts values as arguments' do
subject.set([1,2,3])
expect { subject.subtract(2,3) }
.to change { subject.to_a }.to contain_exactly(1)
end

it 'subtracts values as an enumerable' do
subject.set([1,2,3])
expect { subject.subtract([2,3]) }
.to change { subject.to_a }.to contain_exactly(1)
end

it 'subtracts token values' do
subject.set([:one, :two, :three])
expect { subject.subtract([:two, :three]) }
.to change { subject.to_a }.to contain_exactly(:one)
end
end

describe '#clear' do
include_context 'with symbol property'
let(:parent_resource) { ActiveTriples::Resource.new }
Expand Down

0 comments on commit 16b0b96

Please sign in to comment.