Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 33 additions & 32 deletions lib/rdf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,53 @@

module RDF
# RDF mixins
autoload :Countable, 'rdf/mixin/countable'
autoload :Durable, 'rdf/mixin/durable'
autoload :Enumerable, 'rdf/mixin/enumerable'
autoload :Indexable, 'rdf/mixin/indexable'
autoload :Mutable, 'rdf/mixin/mutable'
autoload :Queryable, 'rdf/mixin/queryable'
autoload :Readable, 'rdf/mixin/readable'
autoload :TypeCheck, 'rdf/mixin/type_check'
autoload :Writable, 'rdf/mixin/writable'
autoload :Countable, 'rdf/mixin/countable'
autoload :Durable, 'rdf/mixin/durable'
autoload :Enumerable, 'rdf/mixin/enumerable'
autoload :Indexable, 'rdf/mixin/indexable'
autoload :Mutable, 'rdf/mixin/mutable'
autoload :Queryable, 'rdf/mixin/queryable'
autoload :Readable, 'rdf/mixin/readable'
autoload :TypeCheck, 'rdf/mixin/type_check'
autoload :Transactable, 'rdf/mixin/transactable'
autoload :Writable, 'rdf/mixin/writable'

# RDF objects
autoload :Graph, 'rdf/model/graph'
autoload :IRI, 'rdf/model/uri'
autoload :Literal, 'rdf/model/literal'
autoload :Node, 'rdf/model/node'
autoload :Resource, 'rdf/model/resource'
autoload :Statement, 'rdf/model/statement'
autoload :URI, 'rdf/model/uri'
autoload :Value, 'rdf/model/value'
autoload :Term, 'rdf/model/term'
autoload :Graph, 'rdf/model/graph'
autoload :IRI, 'rdf/model/uri'
autoload :Literal, 'rdf/model/literal'
autoload :Node, 'rdf/model/node'
autoload :Resource, 'rdf/model/resource'
autoload :Statement, 'rdf/model/statement'
autoload :URI, 'rdf/model/uri'
autoload :Value, 'rdf/model/value'
autoload :Term, 'rdf/model/term'

# RDF collections
autoload :List, 'rdf/model/list'
autoload :List, 'rdf/model/list'

# RDF serialization
autoload :Format, 'rdf/format'
autoload :Reader, 'rdf/reader'
autoload :ReaderError, 'rdf/reader'
autoload :Writer, 'rdf/writer'
autoload :WriterError, 'rdf/writer'
autoload :Format, 'rdf/format'
autoload :Reader, 'rdf/reader'
autoload :ReaderError, 'rdf/reader'
autoload :Writer, 'rdf/writer'
autoload :WriterError, 'rdf/writer'

# RDF serialization formats
autoload :NTriples, 'rdf/ntriples'
autoload :NQuads, 'rdf/nquads'
autoload :NTriples, 'rdf/ntriples'
autoload :NQuads, 'rdf/nquads'

# RDF storage
autoload :Changeset, 'rdf/changeset'
autoload :Dataset, 'rdf/model/dataset'
autoload :Repository, 'rdf/repository'
autoload :Transaction, 'rdf/transaction'
autoload :Changeset, 'rdf/changeset'
autoload :Dataset, 'rdf/model/dataset'
autoload :Repository, 'rdf/repository'
autoload :Transaction, 'rdf/transaction'

# RDF querying
autoload :Query, 'rdf/query'
autoload :Query, 'rdf/query'

# RDF vocabularies
autoload :Vocabulary, 'rdf/vocabulary'
autoload :Vocabulary, 'rdf/vocabulary'
autoload :StrictVocabulary, 'rdf/vocabulary'
VOCABS = Dir.glob(File.join(File.dirname(__FILE__), 'rdf', 'vocab', '*.rb')).map { |f| File.basename(f)[0...-(File.extname(f).size)].to_sym } rescue []

Expand Down
2 changes: 1 addition & 1 deletion lib/rdf/mixin/mutable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def apply_changeset(changeset)
# @raise [NotImplementederror] when snapshots aren't implemented for the
# class
def snapshot
raise NotImplementedError, " #{self.class} does not implement snapshots"
raise NotImplementedError, "#{self.class} does not implement snapshots"
end

##
Expand Down
94 changes: 94 additions & 0 deletions lib/rdf/mixin/transactable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
module RDF
##
# A transaction application mixin.
#
# Classes that include this module must provide a `#begin_transaction` method
# returning an {RDF::Transaction}.
#
# @example running a read/write transaction with block syntax
# repository = RDF::Repository.new # or other transactable
#
# repository.transaction(mutable: true) do |tx|
# tx.insert [:node, RDF.type, RDF::OWL.Thing]
# # ...
# end
#
# @see RDF::Transaction
# @since 2.0.0
module Transactable
##
# Executes the given block in a transaction.
#
# @example running a transaction
# repository.transaction do |tx|
# tx.insert [RDF::URI("http://rubygems.org/gems/rdf"), RDF::RDFS.label, "RDF.rb"]
# end
#
# Raising an error within the transaction block causes automatic rollback.
#
# @param mutable [Boolean]
# allows changes to the transaction, otherwise it is a read-only snapshot of the underlying repository.
# @yield [tx]
# @yieldparam [RDF::Transaction] tx
# @yieldreturn [void] ignored
# @return [self]
# @see RDF::Transaction
# @since 0.3.0
def transaction(mutable: false, &block)
tx = begin_transaction(mutable: mutable)
begin
case block.arity
when 1 then block.call(tx)
else tx.instance_eval(&block)
end
rescue => error
rollback_transaction(tx)
raise error
end
commit_transaction(tx)
self
end
alias_method :transact, :transaction

protected

##
# Begins a new transaction.
#
# Subclasses implementing transaction-capable storage adapters may wish
# to override this method in order to begin a transaction against the
# underlying storage.
#
# @param mutable [Boolean] Create a mutable or immutable transaction.
# @param graph_name [Boolean] A default graph name for statements inserted
# or deleted (default: nil)
# @return [RDF::Transaction]
def begin_transaction(mutable: false, graph_name: nil)
raise NotImplementedError
end

##
# Rolls back the given transaction.
#
# @param [RDF::Transaction] tx
# @return [void] ignored
# @since 0.3.0
def rollback_transaction(tx)
tx.rollback
end

##
# Commits the given transaction.
#
# Subclasses implementing transaction-capable storage adapters may wish
# to override this method in order to commit the given transaction to
# the underlying storage.
#
# @param [RDF::Transaction] tx
# @return [void] ignored
# @since 0.3.0
def commit_transaction(tx)
tx.execute
end
end
end
10 changes: 10 additions & 0 deletions lib/rdf/model/graph.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Graph
include RDF::Enumerable
include RDF::Queryable
include RDF::Mutable
include RDF::Transactable

##
# Returns the options passed to this graph when it was constructed.
Expand Down Expand Up @@ -335,10 +336,19 @@ def clear_statements
@data.delete(graph_name: graph_name || false)
end

##
# @private
# Opens a transaction over the graph
# @see RDF::Transactable#begin_transaction
def begin_transaction(mutable: false, graph_name: @graph_name)
@data.send(:begin_transaction, mutable: mutable, graph_name: graph_name)
end

protected :query_pattern
protected :insert_statement
protected :delete_statement
protected :clear_statements
protected :begin_transaction

##
# @private
Expand Down
96 changes: 23 additions & 73 deletions lib/rdf/repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ module RDF
class Repository < Dataset
include RDF::Mutable

include RDF::Transactable

DEFAULT_TX_CLASS = RDF::Transaction

##
Expand Down Expand Up @@ -176,78 +178,16 @@ def isolation_level
def snapshot
raise NotImplementedError.new("#{self.class}#snapshot")
end

protected

##
# Executes the given block in a transaction.
#
# @example
# repository.transaction do |tx|
# tx.insert [RDF::URI("http://rubygems.org/gems/rdf"), RDF::RDFS.label, "RDF.rb"]
# end
#
# @param mutable [Boolean]
# allows changes to the transaction, otherwise it is a read-only snapshot of the underlying repository.
# @yield [tx]
# @yieldparam [RDF::Transaction] tx
# @yieldreturn [void] ignored
# @return [self]
# @see RDF::Transaction
# @since 0.3.0
def transaction(mutable: false, &block)
tx = begin_transaction(mutable: mutable)
begin
case block.arity
when 1 then block.call(tx)
else tx.instance_eval(&block)
end
rescue => error
rollback_transaction(tx)
raise error
##
# @private
# @see RDF::Transactable#begin_transaction
# @since 0.3.0
def begin_transaction(mutable: false, graph_name: nil)
@tx_class.new(self, mutable: mutable, graph_name: graph_name)
end
commit_transaction(tx)
self
end
alias_method :transact, :transaction

protected

##
# Begins a new transaction.
#
# Subclasses implementing transaction-capable storage adapters may wish
# to override this method in order to begin a transaction against the
# underlying storage.
#
# @param mutable [Boolean] Create a mutable or immutable transaction.
# @return [RDF::Transaction]
# @since 0.3.0
def begin_transaction(mutable: false)
@tx_class.new(self, mutable: mutable)
end

##
# Rolls back the given transaction.
#
# @param [RDF::Transaction] tx
# @return [void] ignored
# @since 0.3.0
def rollback_transaction(tx)
tx.rollback
end

##
# Commits the given transaction.
#
# Subclasses implementing transaction-capable storage adapters may wish
# to override this method in order to commit the given transaction to
# the underlying storage.
#
# @param [RDF::Transaction] tx
# @return [void] ignored
# @since 0.3.0
def commit_transaction(tx)
tx.execute
end

##
# @see RDF::Repository
Expand Down Expand Up @@ -533,14 +473,24 @@ def initialize(*)

def insert_statement(statement)
@snapshot = @snapshot.class
.new(data: @snapshot.send(:insert_to, @snapshot.send(:data), statement))
.new(data: @snapshot.send(:insert_to,
@snapshot.send(:data),
process_statement(statement)))
end

def delete_statement(statement)
@snapshot = @snapshot.class
.new(data: @snapshot.send(:delete_from, @snapshot.send(:data), statement))
.new(data: @snapshot.send(:delete_from,
@snapshot.send(:data),
process_statement(statement)))
end


##
# @see RDF::Dataset#isolation_level
def isolation_level
:serializable
end

def execute
raise TransactionError, 'Cannot execute a rolled back transaction. ' \
'Open a new one instead.' if @rolledback
Expand Down
Loading