Skip to content

Commit

Permalink
Factored out the append-only bits of RDF::Mutable into RDF::Writable.
Browse files Browse the repository at this point in the history
In particular, the #<< and #insert methods are now defined in RDF::Writable instead of RDF::Mutable (though the latter includes everything from the former, of course).

This change benefits stream-oriented classes, such as RDF::Writer, which will want to mix in RDF::Writable but not the rest of RDF::Mutable.
  • Loading branch information
artob committed Jun 16, 2010
1 parent 31381e4 commit 053bec3
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 104 deletions.
150 changes: 46 additions & 104 deletions lib/rdf/mixin/mutable.rb
Expand Up @@ -33,7 +33,7 @@ def immutable?
#
# @param [String, #to_s] filename
# @param [Hash{Symbol => Object}] options
# @return [Integer] the number of inserted RDF statements
# @return [void]
def load(filename, options = {})
raise TypeError.new("#{self} is immutable") if immutable?

Expand All @@ -48,58 +48,66 @@ def load(filename, options = {})
statements.size
else
insert_statements(reader)
nil # FIXME
nil
end
end
end

alias_method :load!, :load

##
# Inserts an RDF statement into `self`.
# Inserts RDF data into `self`.
#
# @param [RDF::Statement, Array<RDF::Value>, #to_a] statement
# @param [RDF::Enumerable, RDF::Statement] data
# @raise [TypeError] if `self` is immutable
# @return [Mutable]
def <<(statement)
# @see RDF::Writable#<<
def <<(data)
raise TypeError.new("#{self} is immutable") if immutable?

insert_statement(create_statement(statement))

return self
super # RDF::Writable#<<
end

##
# Inserts RDF statements into `self`.
#
# @param [Enumerable<RDF::Statement>] statements
# @param [Array<RDF::Statement>] statements
# @raise [TypeError] if `self` is immutable
# @return [Mutable]
# @see RDF::Writable#insert
def insert(*statements)
raise TypeError.new("#{self} is immutable") if immutable?

statements.map! do |value|
case
when value.respond_to?(:each_statement)
insert_statements(value)
nil
when (statement = create_statement(value)).valid?
statement
else
raise ArgumentError.new("not a valid statement: #{value.inspect}")
super # RDF::Writable#insert
end

##
# Updates RDF statements in `self`.
#
# `#update([subject, predicate, object])` is equivalent to
# `#delete([subject, predicate, nil])` followed by
# `#insert([subject, predicate, object])` unless `object` is `nil`.
#
# @param [Enumerable<RDF::Statement>] statements
# @raise [TypeError] if `self` is immutable
# @return [Mutable]
def update(*statements)
raise TypeError.new("#{self} is immutable") if immutable?

statements.each do |statement|
if (statement = create_statement(statement))
delete([statement.subject, statement.predicate, nil])
insert(statement) if statement.has_object?
end
end
statements.compact!
insert_statements(statements) unless statements.empty?

return self
end

alias_method :insert!, :insert
alias_method :update!, :update

##
# Deletes RDF statements from `self`.
#
# @param [Enumerable<Statement>] statements
# @param [Enumerable<RDF::Statement>] statements
# @raise [TypeError] if `self` is immutable
# @return [Mutable]
def delete(*statements)
Expand All @@ -125,88 +133,41 @@ def delete(*statements)

alias_method :delete!, :delete

##
# Updates RDF statements in `self`.
#
# `#update([subject, predicate, object])` is equivalent to
# `#delete([subject, predicate, nil])` followed by
# `#insert([subject, predicate, object])` unless `object` is `nil`.
#
# @param [Enumerable<RDF::Statement>] statements
# @raise [TypeError] if `self` is immutable
# @return [Mutable]
def update(*statements)
raise TypeError.new("#{self} is immutable") if immutable?

statements.each do |statement|
if (statement = create_statement(statement))
delete([statement.subject, statement.predicate, nil])
insert(statement) if statement.has_object?
end
end
end

alias_method :update!, :update

##
# Deletes all RDF statements from `self`.
#
# @raise [TypeError] if `self` is immutable
# @return [Mutable]
def clear
raise TypeError.new("#{self} is immutable") if immutable?

if respond_to?(:clear_statements)
clear_statements
else
each_statement do |statement|
delete_statement(statement)
end
delete_statements(self)
end
self

return self
end

alias_method :clear!, :clear

##
# Transforms various input into an `RDF::Statement` instance.
#
# @param [RDF::Statement, Hash, Array, #to_a] statement
# @return [RDF::Statement]
def create_statement(statement)
case statement
when Statement then statement
when Hash then Statement.new(statement)
when Array then Statement.new(*statement)
else raise ArgumentError.new # FIXME
end
end
protected

##
# Inserts an RDF statement into the underlying storage.
# Deletes the given RDF statements from the underlying storage.
#
# Subclasses of {RDF::Repository} must implement this method (except in
# case they are immutable).
# Defaults to invoking {#delete_statement} for each given statement.
#
# @param [RDF::Statement] statement
# @return [void]
# @abstract
def insert_statement(statement)
raise NotImplementedError
end

##
# Inserts a list of RDF statement into the underlying storage.
# Subclasses of {RDF::Repository} may override this method if they are
# capable of more efficiently deleting multiple statements at once.
#
# Subclasses of {RDF::Repository} may implement this method if they can
# efficiently insert multiple statements at once. This will otherwise
# default to invoking {#insert_statement} for each given statement.
#
# @param [RDF::Enumerable, #each] statements
# @param [RDF::Enumerable] statements
# @return [void]
def insert_statements(statements)
def delete_statements(statements)
each = statements.respond_to?(:each_statement) ? :each_statement : :each
statements.__send__(each) do |statement|
insert_statement(statement)
delete_statement(statement)
end
end

Expand All @@ -220,29 +181,10 @@ def insert_statements(statements)
# @return [void]
# @abstract
def delete_statement(statement)
raise NotImplementedError
end

##
# Deletes a list of RDF statement from the underlying storage.
#
# Subclasses of {RDF::Repository} may implement this method if they can
# efficiently delete multiple statements at once. This will otherwise
# default to invoking {#delete_statement} for each given statement.
#
# @param [RDF::Enumerable, #each] statements
# @return [void]
def delete_statements(statements)
each = statements.respond_to?(:each_statement) ? :each_statement : :each
statements.__send__(each) do |statement|
delete_statement(statement)
end
raise NotImplementedError.new("#{self.class}#delete_statement")
end

protected :create_statement
protected :insert_statement
protected :insert_statements
protected :delete_statement
protected :delete_statements
protected :delete_statement
end
end
2 changes: 2 additions & 0 deletions lib/rdf/mixin/readable.rb
@@ -1,6 +1,8 @@
module RDF
##
module Readable
extend RDF::Util::Aliasing::LateBound

##
# Returns `true` if `self` is readable.
#
Expand Down
95 changes: 95 additions & 0 deletions lib/rdf/mixin/writable.rb
@@ -1,6 +1,8 @@
module RDF
##
module Writable
extend RDF::Util::Aliasing::LateBound

##
# Returns `true` if `self` is writable.
#
Expand All @@ -9,5 +11,98 @@ module Writable
def writable?
true
end

##
# Inserts RDF data into `self`.
#
# @param [RDF::Enumerable, RDF::Statement] statement
# @return [Writable]
def <<(statement)
insert_statement(create_statement(statement)) # FIXME

return self
end

##
# Inserts RDF statements into `self`.
#
# @param [Array<RDF::Statement>] statements
# @return [Writable]
def insert(*statements)
statements.map! do |value|
case
when value.respond_to?(:each_statement)
insert_statements(value)
nil
when (statement = create_statement(value)).valid?
statement
else
raise ArgumentError.new("not a valid statement: #{value.inspect}")
end
end
statements.compact!
insert_statements(statements) unless statements.empty?

return self
end

alias_method :insert!, :insert

protected

##
# Inserts the given RDF statements into the underlying storage or output
# stream.
#
# Defaults to invoking {#insert_statement} for each given statement.
#
# Subclasses of {RDF::Repository} may override this method if they are
# capable of more efficiently inserting multiple statements at once.
#
# Subclasses of {RDF::Writer} don't generally need to implement this
# method.
#
# @param [RDF::Enumerable] statements
# @return [void]
def insert_statements(statements)
each = statements.respond_to?(:each_statement) ? :each_statement : :each
statements.__send__(each) do |statement|
insert_statement(statement)
end
end

##
# Inserts an RDF statement into the underlying storage or output stream.
#
# Subclasses of {RDF::Repository} must implement this method (except in
# case they are immutable).
#
# Subclasses of {RDF::Writer} must implement this method.
#
# @param [RDF::Statement] statement
# @return [void]
# @abstract
def insert_statement(statement)
raise NotImplementedError.new("#{self.class}#insert_statement")
end

##
# Transforms various input into an `RDF::Statement` instance.
#
# @param [RDF::Statement, Hash, Array, #to_a] statement
# @return [RDF::Statement]
def create_statement(statement)
# TODO: move this to RDF::Statement.construct or the like.
case statement
when Statement then statement
when Hash then Statement.new(statement)
when Array then Statement.new(*statement)
else raise ArgumentError.new # FIXME
end
end

protected :insert_statements
protected :insert_statement
protected :create_statement
end
end

0 comments on commit 053bec3

Please sign in to comment.