Skip to content

Commit 2b7dca0

Browse files
doriantaylorgkellogg
authored andcommitted
ugly but all tests pass
1 parent b597ed8 commit 2b7dca0

7 files changed

Lines changed: 136 additions & 73 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ Gemfile.lock
1212
benchmark/
1313
/.byebug_history
1414
/coverage/
15+
\#*\#
16+
.\#*

lib/rdf/changeset.rb

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ module RDF
3131
#
3232
# @since 2.0.0
3333
class Changeset
34-
include RDF::Mutable
34+
# include RDF::Mutable
35+
include RDF::Util::Coercions
3536

3637
##
37-
# Applies a changeset to the given mutable RDF::Enumerable .
38+
# Applies a changeset to the given {RDF::Mutable} object.
3839
#
3940
# @param [RDF::Mutable] mutable
4041
# @param [Hash{Symbol => Object}] options
@@ -86,9 +87,9 @@ def initialize(insert: [], delete: [], &block)
8687
end
8788

8889
##
89-
# Returns `false` to indicate that this changeset is append-only.
90+
# Returns +false+ to indicate that this changeset is append-only.
9091
#
91-
# Changesets do not support the `RDF::Enumerable` protocol directly.
92+
# Changesets do not support the +RDF::Enumerable+ protocol directly.
9293
# To enumerate the RDF statements to be inserted or deleted, use the
9394
# {RDF::Changeset#inserts} and {RDF::Changeset#deletes} accessors.
9495
#
@@ -98,6 +99,14 @@ def readable?
9899
false
99100
end
100101

102+
def writable?
103+
false
104+
end
105+
106+
def mutable?
107+
false
108+
end
109+
101110
##
102111
# Applies this changeset to the given mutable RDF::Enumerable.
103112
#
@@ -111,7 +120,7 @@ def apply(mutable, **options)
111120
end
112121

113122
##
114-
# @return [Boolean] `true` iff inserts and deletes are both empty
123+
# @return [Boolean] +true+ iff inserts and deletes are both empty
115124
def empty?
116125
deletes.empty? && inserts.empty?
117126
end
@@ -127,41 +136,77 @@ def inspect
127136

128137
##
129138
# Outputs a developer-friendly representation of this changeset to
130-
# `stderr`.
139+
# +$stderr+.
131140
#
132141
# @return [void]
133142
def inspect!
134143
$stderr.puts(self.inspect)
135144
end
136145

137-
protected
138-
139146
##
140-
# Appends an RDF statement to the sequence to insert when applied.
147+
# Returns the sum of both the +inserts+ and +deletes+ counts.
141148
#
142-
# @param [RDF::Statement] statement
143-
# @return [void]
144-
# @see RDF::Writable#insert_statement
145-
def insert_statement(statement)
146-
self.inserts << statement
149+
# @return [Integer]
150+
def count
151+
inserts.count + deletes.count
147152
end
148153

149-
##
150-
# Appends an RDF statement to the sequence to delete when applied.
154+
# Append statements to +inserts+. Statements _should_ be constant
155+
# as variable statements will at best be ignored or at worst raise
156+
# an error when applied.
151157
#
152-
# @param [RDF::Statement] statement
153-
# @return [void]
154-
# @see RDF::Mutable#delete_statement
155-
def delete_statement(statement)
156-
self.deletes << statement
158+
# @param statements [Enumerable, # RDF::Statement] Some statements
159+
# @return [self]
160+
def insert(*statements)
161+
process_statements(statements) do |stmts|
162+
append_statements :inserts, stmts
163+
end
164+
165+
self
166+
end
167+
alias_method :insert!, :insert
168+
alias_method :<<, :insert
169+
170+
# Append statements to +deletes+. Statements _may_ contain
171+
# variables, although support will depend on the {RDF::Mutable}
172+
# target.
173+
#
174+
# @param statements [Enumerable, RDF::Statement] Some statements
175+
# @return [self]
176+
def delete(*statements)
177+
process_statements(statements) do |stmts|
178+
append_statements :deletes, stmts
179+
end
180+
181+
self
157182
end
183+
alias_method :delete!, :delete
184+
alias_method :>>, :delete
185+
186+
private
158187

159-
# This simply returns its argument as a query in order to trick
160-
# {RDF::Mutable#delete} into working.
161-
def query(stmt)
162-
RDF::Query.new RDF::Query::Pattern.from(stmt)
188+
##
189+
# Append statements to the appropriate target. This is a crappy
190+
# little shim to go in between the other shim and the target.
191+
#
192+
# @param target [Symbol] the method to send
193+
# @param arg [Enumerable, RDF::Statement]
194+
#
195+
def append_statements(target, arg)
196+
# coerce to an enumerator
197+
stmts = case
198+
when arg.is_a?(RDF::Statement)
199+
[arg]
200+
when arg.respond_to?(:each_statement)
201+
arg.each_statement
202+
when arg.respond_to?(:each)
203+
arg
204+
else
205+
raise ArgumentError, "Invalid statement: #{arg.class}"
206+
end
207+
208+
stmts.each { |s| send(target) << s }
163209
end
164210

165-
undef_method :load, :update, :clear
166211
end # Changeset
167212
end # RDF

lib/rdf/mixin/mutable.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module Mutable
99
extend RDF::Util::Aliasing::LateBound
1010
include RDF::Readable
1111
include RDF::Writable
12+
include RDF::Util::Coercions
1213

1314
##
1415
# Returns `true` if `self` is mutable.

lib/rdf/mixin/writable.rb

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module RDF
77
# @see RDF::Repository
88
module Writable
99
extend RDF::Util::Aliasing::LateBound
10+
include RDF::Util::Coercions
1011

1112
##
1213
# Returns `true` if `self` is writable.
@@ -58,53 +59,13 @@ def <<(data)
5859
# @param [Enumerable<RDF::Statement>] statements
5960
# @return [self]
6061
def insert(*statements)
61-
process_statements(statements) { |value| insert_statements(value) }
62+
process_statements(statements) { |value| insert_statements value }
6263

6364
return self
6465
end
6566
alias_method :insert!, :insert
6667

6768
protected
68-
69-
##
70-
# Coerce a set of arguments into {RDF::Statement} objects and then
71-
# operate over them with a block.
72-
#
73-
# @example
74-
# process_statements(statements) { |value| do_something(value) }
75-
#
76-
# @param statements [#map] The arbitrary-ish input to be manipulated
77-
# @param query [false, true] Whether to call +query+ before the block
78-
# @param constant [false, true] Whether to test if the statements
79-
# are constant
80-
# @yield [RDF::Statement, RDF::Enumerable]
81-
# @return statements
82-
def process_statements(statements, query: false, constant: false, &block)
83-
raise ArgumentError, 'expecting a block' unless block_given?
84-
85-
statements = statements.map do |value|
86-
case
87-
when value.respond_to?(:each_statement)
88-
block.call(value)
89-
nil
90-
when (statement = Statement.from(value)) &&
91-
(!constant || statement.constant?)
92-
statement
93-
when query
94-
block.call(query(value))
95-
nil
96-
else
97-
raise ArgumentError, "Not a valid statement: #{value.inspect}"
98-
end
99-
end.compact
100-
101-
block.call(statements) unless statements.empty?
102-
103-
# eh might as well return these
104-
statements
105-
end
106-
107-
10869
##
10970
# Inserts statements from the given RDF reader into the underlying
11071
# storage or output stream.

lib/rdf/util.rb

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
module RDF; module Util
2-
autoload :Aliasing, 'rdf/util/aliasing'
3-
autoload :Cache, 'rdf/util/cache'
4-
autoload :File, 'rdf/util/file'
5-
autoload :Logger, 'rdf/util/logger'
6-
autoload :UUID, 'rdf/util/uuid'
2+
autoload :Aliasing, 'rdf/util/aliasing'
3+
autoload :Cache, 'rdf/util/cache'
4+
autoload :File, 'rdf/util/file'
5+
autoload :Logger, 'rdf/util/logger'
6+
autoload :UUID, 'rdf/util/uuid'
7+
autoload :Coercions, 'rdf/util/coercions'
78
end; end

lib/rdf/util/coercions.rb

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
module RDF
2+
module Util
3+
module Coercions
4+
# This is a provisional module intended to house input
5+
# coercions. Currently the only coercion is a statement
6+
# preprocessor that is used in e.g. {RDF::Writable#insert} and
7+
# {RDF::Mutable#delete}.
8+
9+
##
10+
# Coerce a set of arguments into {RDF::Statement} objects and then
11+
# operate over them with a block.
12+
#
13+
# @example
14+
# process_statements(statements) { |value| do_something(value) }
15+
#
16+
# @param statements [#map] The arbitrary-ish input to be manipulated
17+
# @param query [false, true] Whether to call +query+ before the block
18+
# @param constant [false, true] Whether to test if the statements
19+
# are constant
20+
# @yield [RDF::Statement, RDF::Enumerable]
21+
# @return statements
22+
def process_statements(statements, query: false, constant: false, &block)
23+
raise ArgumentError, 'expecting a block' unless block_given?
24+
25+
statements = statements.map do |value|
26+
case
27+
when value.respond_to?(:each_statement)
28+
block.call(value)
29+
nil
30+
when (statement = Statement.from(value)) &&
31+
(!constant || statement.constant?)
32+
statement
33+
when query
34+
# XXX note that this only makes sense when the module is include()d
35+
block.call(query(value))
36+
nil
37+
else
38+
raise ArgumentError, "Not a valid statement: #{value.inspect}"
39+
end
40+
end.compact
41+
42+
block.call(statements) unless statements.empty?
43+
44+
# eh might as well return these
45+
statements
46+
end
47+
48+
# so you can include or call
49+
extend self
50+
end
51+
end
52+
end

spec/changeset_spec.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
its(:deletes) { is_expected.to be_a(RDF::Enumerable) }
2929
its(:inserts) { is_expected.to be_a(RDF::Enumerable) }
3030

31-
it { is_expected.to be_mutable }
31+
it { is_expected.to_not be_mutable }
3232
it { is_expected.to_not be_readable }
3333

3434
it "does not respond to #load" do
@@ -135,4 +135,5 @@
135135
.to change { subject.inserts }.to contain_exactly(*statements)
136136
end
137137
end
138+
138139
end

0 commit comments

Comments
 (0)