redlander 0.2.2 → 0.3.1
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.
- data/README.rdoc +8 -4
- data/lib/redlander.rb +17 -22
- data/lib/redlander/error_container.rb +28 -27
- data/lib/redlander/model.rb +23 -11
- data/lib/redlander/model_proxy.rb +41 -29
- data/lib/redlander/node.rb +36 -29
- data/lib/redlander/parser.rb +15 -17
- data/lib/redlander/parser_proxy.rb +11 -10
- data/lib/redlander/serializer.rb +15 -10
- data/lib/redlander/statement.rb +47 -91
- data/lib/redlander/storage.rb +14 -36
- data/lib/redlander/stream.rb +57 -0
- data/lib/redlander/stream.rb~ +43 -0
- data/lib/redlander/stream_enumerator.rb +17 -0
- data/lib/redlander/stream_enumerator.rb~ +15 -0
- data/lib/redlander/uri.rb +29 -0
- data/lib/redlander/uri.rb~ +12 -0
- data/lib/redlander/version.rb +1 -1
- data/spec/integration/memory_leak_spec.rb +24 -0
- data/spec/integration/memory_leak_spec.rb~ +24 -0
- data/spec/redlander/model_spec.rb +6 -14
- data/spec/redlander/statement_spec.rb +0 -9
- metadata +43 -24
- data/ext/README +0 -1
- data/ext/extconf.rb +0 -17
- data/ext/redland-pre.i +0 -6
- data/ext/redland-types.i +0 -9
- data/ext/redland_wrap.c +0 -8052
- data/lib/redlander/statement_iterator.rb +0 -37
data/README.rdoc
CHANGED
@@ -8,7 +8,7 @@ Installing Redlander is simple:
|
|
8
8
|
|
9
9
|
$ gem install redlander
|
10
10
|
|
11
|
-
Note, that
|
11
|
+
Note, that you will have to install Redlander (librdf) for Redlander to work.
|
12
12
|
|
13
13
|
= Usage
|
14
14
|
|
@@ -34,17 +34,21 @@ Most of Redlander functionality is accessable via these statements. The API is a
|
|
34
34
|
$ st = Redlander::Statement.new(:subject => s, :predicate => p, :object => "another label")
|
35
35
|
$ m.statements.add(st)
|
36
36
|
|
37
|
-
m.statements.size # => 2
|
37
|
+
$ m.statements.size # => 2
|
38
|
+
|
39
|
+
$ m.statements.each { |st| puts st }
|
38
40
|
|
39
41
|
Finding statements:
|
40
42
|
|
41
43
|
m.statements.find(:first, :object => "subject!")
|
42
44
|
m.statements.all(:object => "another label")
|
43
|
-
m.statements.find(:all, :object => "subject!") { |statement|
|
45
|
+
m.statements.find(:all, :object => "subject!").each { |statement|
|
44
46
|
puts statement.subject
|
45
47
|
}
|
46
48
|
|
47
|
-
|
49
|
+
Note that "m.statements.each" is "lazy", while "m.statements.all" (and other finders) are not.
|
50
|
+
|
51
|
+
For more details refer to the documentation of Model, Statement, Storage and other classes of Redlander.
|
48
52
|
|
49
53
|
= Exceptions
|
50
54
|
|
data/lib/redlander.rb
CHANGED
@@ -10,13 +10,27 @@ module Redlander
|
|
10
10
|
|
11
11
|
class RedlandError < RuntimeError; end
|
12
12
|
|
13
|
-
|
13
|
+
autoload :ErrorContainer, 'redlander/error_container'
|
14
|
+
autoload :Uri, 'redlander/uri'
|
15
|
+
autoload :Parser, 'redlander/parser'
|
16
|
+
autoload :ParserProxy, 'redlander/parser_proxy'
|
17
|
+
autoload :Serializer, 'redlander/serializer'
|
18
|
+
autoload :Model, 'redlander/model'
|
19
|
+
autoload :ModelProxy, 'redlander/model_proxy'
|
20
|
+
autoload :Node, 'redlander/node'
|
21
|
+
autoload :Stream, 'redlander/stream'
|
22
|
+
autoload :Storage, 'redlander/storage'
|
23
|
+
autoload :ParsingInstanceMethods, 'redlander/parser'
|
24
|
+
autoload :SerializingInstanceMethods, 'redlander/serializer'
|
25
|
+
autoload :StreamEnumerator, 'redlander/stream_enumerator'
|
26
|
+
autoload :Statement, 'redlander/statement'
|
14
27
|
|
28
|
+
class << self
|
15
29
|
def rdf_world
|
16
30
|
unless @rdf_world
|
17
31
|
@rdf_world = Redland.librdf_new_world
|
18
|
-
raise RedlandError.new("Could not create a new RDF world")
|
19
|
-
ObjectSpace.define_finalizer(
|
32
|
+
raise RedlandError.new("Could not create a new RDF world") if @rdf_world.null?
|
33
|
+
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_world(@rdf_world) })
|
20
34
|
Redland.librdf_world_open(@rdf_world)
|
21
35
|
end
|
22
36
|
@rdf_world
|
@@ -38,24 +52,5 @@ module Redlander
|
|
38
52
|
opts << "#{key}='#{value}'"
|
39
53
|
}.join(',')
|
40
54
|
end
|
41
|
-
|
42
|
-
# Helper method to create an instance of rdfuri.
|
43
|
-
# For internal use only!
|
44
|
-
def to_rdf_uri(uri)
|
45
|
-
return nil if uri.nil?
|
46
|
-
uri = uri.is_a?(URI) ? uri.to_s : uri
|
47
|
-
rdf_uri = Redland.librdf_new_uri(rdf_world, uri)
|
48
|
-
raise RedlandError.new("Failed to create URI from '#{uri}'") unless rdf_uri
|
49
|
-
ObjectSpace.define_finalizer(rdf_uri, proc { Redland.librdf_free_uri(rdf_uri) })
|
50
|
-
rdf_uri
|
51
|
-
end
|
52
|
-
|
53
55
|
end
|
54
|
-
|
55
|
-
require 'redlander/error_container'
|
56
|
-
require 'redlander/statement_iterator'
|
57
|
-
require 'redlander/parser'
|
58
|
-
require 'redlander/serializer'
|
59
|
-
require 'redlander/model'
|
60
|
-
require 'redlander/node'
|
61
56
|
end
|
@@ -1,41 +1,42 @@
|
|
1
|
-
module
|
1
|
+
module Redlander
|
2
|
+
module ErrorContainer
|
3
|
+
class Errors
|
4
|
+
include Enumerable
|
2
5
|
|
3
|
-
|
4
|
-
|
6
|
+
def initialize
|
7
|
+
@container = []
|
8
|
+
end
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
|
10
|
+
def add(error_message)
|
11
|
+
if @container.include?(error_message)
|
12
|
+
@container
|
13
|
+
else
|
14
|
+
@container << error_message
|
15
|
+
end
|
16
|
+
end
|
9
17
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@container << error_message
|
18
|
+
def each
|
19
|
+
@container.each do |err|
|
20
|
+
yield err
|
21
|
+
end
|
15
22
|
end
|
16
|
-
end
|
17
23
|
|
18
|
-
|
19
|
-
|
20
|
-
yield err
|
24
|
+
def empty?
|
25
|
+
@container.empty?
|
21
26
|
end
|
22
|
-
end
|
23
27
|
|
24
|
-
|
25
|
-
|
26
|
-
|
28
|
+
def clear
|
29
|
+
@container.clear
|
30
|
+
end
|
27
31
|
|
28
|
-
|
29
|
-
|
32
|
+
def size
|
33
|
+
@container.size
|
34
|
+
end
|
30
35
|
end
|
31
36
|
|
32
|
-
def
|
33
|
-
@
|
37
|
+
def errors
|
38
|
+
@errors ||= Errors.new
|
34
39
|
end
|
35
|
-
end
|
36
40
|
|
37
|
-
def errors
|
38
|
-
@errors ||= Errors.new
|
39
41
|
end
|
40
|
-
|
41
42
|
end
|
data/lib/redlander/model.rb
CHANGED
@@ -1,23 +1,18 @@
|
|
1
|
-
require 'redlander/storage'
|
2
|
-
require 'redlander/model_proxy'
|
3
|
-
|
4
1
|
module Redlander
|
5
|
-
|
6
2
|
class Model
|
7
|
-
|
8
3
|
include Redlander::ParsingInstanceMethods
|
9
4
|
include Redlander::SerializingInstanceMethods
|
10
5
|
|
11
6
|
attr_reader :rdf_model
|
12
7
|
|
13
8
|
# Create a new RDF model.
|
14
|
-
# For explanation of options, read Storage.
|
9
|
+
# For explanation of options, read Storage.initialize
|
15
10
|
def initialize(options = {})
|
16
|
-
@
|
11
|
+
@storage = Storage.new(options)
|
17
12
|
|
18
|
-
@rdf_model = Redland.librdf_new_model(Redlander.rdf_world, @rdf_storage, "")
|
19
|
-
raise RedlandError.new("Failed to create a new model")
|
20
|
-
ObjectSpace.define_finalizer(
|
13
|
+
@rdf_model = Redland.librdf_new_model(Redlander.rdf_world, @storage.rdf_storage, "")
|
14
|
+
raise RedlandError.new("Failed to create a new model") if @rdf_model.null?
|
15
|
+
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_model(@rdf_model) })
|
21
16
|
end
|
22
17
|
|
23
18
|
# Statements contained in the model.
|
@@ -28,6 +23,23 @@ module Redlander
|
|
28
23
|
ModelProxy.new(self)
|
29
24
|
end
|
30
25
|
|
31
|
-
|
26
|
+
# Wrap changes to the given model in a transaction.
|
27
|
+
# If an exception is raised in the block, the transaction is rolled back.
|
28
|
+
# (Does not work for all storages, in which case the changes are instanteous).
|
29
|
+
def transaction
|
30
|
+
if block_given?
|
31
|
+
Redland.librdf_model_transaction_start(@rdf_model).zero? || RedlandError.new("Failed to initialize a transaction")
|
32
|
+
yield
|
33
|
+
Redland.librdf_model_transaction_commit(@rdf_model).zero? || RedlandError.new("Failed to commit the transaction")
|
34
|
+
end
|
35
|
+
rescue
|
36
|
+
rollback
|
37
|
+
raise
|
38
|
+
end
|
32
39
|
|
40
|
+
# Rollback the transaction
|
41
|
+
def rollback
|
42
|
+
Redland.librdf_model_transaction_rollback(@rdf_model).zero? || RedlandError.new("Failed to rollback the latest transaction")
|
43
|
+
end
|
44
|
+
end
|
33
45
|
end
|
@@ -1,20 +1,9 @@
|
|
1
|
-
require 'redlander/statement'
|
2
|
-
|
3
1
|
module Redlander
|
4
|
-
|
5
2
|
class ModelProxy
|
3
|
+
include StreamEnumerator
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(model, rdf_stream = nil)
|
5
|
+
def initialize(model)
|
10
6
|
@model = model
|
11
|
-
@rdf_stream = if rdf_stream
|
12
|
-
rdf_stream
|
13
|
-
else
|
14
|
-
Redland.librdf_model_as_stream(@model.rdf_model)
|
15
|
-
end
|
16
|
-
raise RedlandError.new("Failed to create a new stream") unless @rdf_stream
|
17
|
-
ObjectSpace.define_finalizer(@rdf_stream, proc { Redland.librdf_free_stream(@rdf_stream) })
|
18
7
|
end
|
19
8
|
|
20
9
|
# Add a statement to the model.
|
@@ -24,7 +13,31 @@ module Redlander
|
|
24
13
|
#
|
25
14
|
# Returns true on success or false on failure.
|
26
15
|
def add(statement)
|
27
|
-
statement.
|
16
|
+
if statement.valid?
|
17
|
+
Redland.librdf_model_add_statement(@model.rdf_model, statement.rdf_statement).zero?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Delete a statement from the model,
|
22
|
+
# or delete all statements matching the given criteria.
|
23
|
+
# Source can be either
|
24
|
+
# Statement
|
25
|
+
# or
|
26
|
+
# Hash (all keys are optional)
|
27
|
+
# :subject
|
28
|
+
# :predicate
|
29
|
+
# :object
|
30
|
+
def delete(source)
|
31
|
+
statement = case source
|
32
|
+
when Statement
|
33
|
+
source
|
34
|
+
when Hash
|
35
|
+
Statement.new(source)
|
36
|
+
else
|
37
|
+
# TODO
|
38
|
+
raise NotImplementedError.new
|
39
|
+
end
|
40
|
+
Redland.librdf_model_remove_statement(@model.rdf_model, statement.rdf_statement).zero?
|
28
41
|
end
|
29
42
|
|
30
43
|
# Create a statement and add it to the model.
|
@@ -58,32 +71,31 @@ module Redlander
|
|
58
71
|
# :all
|
59
72
|
# :first
|
60
73
|
def find(scope, options = {}, &block)
|
61
|
-
|
62
|
-
rdf_stream = Redland.librdf_model_find_statements(@model.rdf_model, statement.rdf_statement)
|
63
|
-
proxy = self.class.new(@model, rdf_stream)
|
74
|
+
stream = Stream.new(@model, Statement.new(options))
|
64
75
|
|
65
76
|
case scope
|
66
77
|
when :first
|
67
|
-
|
78
|
+
stream.current
|
68
79
|
when :all
|
69
|
-
|
70
|
-
proxy.each(&block)
|
71
|
-
else
|
72
|
-
proxy
|
73
|
-
end
|
80
|
+
stream.tail
|
74
81
|
else
|
75
82
|
raise RedlandError.new("Invalid search scope '#{scope}' specified.")
|
76
83
|
end
|
77
84
|
end
|
78
85
|
|
86
|
+
def first(options = {})
|
87
|
+
find(:first, options)
|
88
|
+
end
|
89
|
+
|
79
90
|
def all(options = {})
|
80
|
-
|
81
|
-
find(:all, options) do |fs|
|
82
|
-
st << fs
|
83
|
-
end
|
84
|
-
end
|
91
|
+
find(:all, options)
|
85
92
|
end
|
86
93
|
|
87
|
-
end
|
88
94
|
|
95
|
+
private
|
96
|
+
|
97
|
+
def reset_stream
|
98
|
+
@stream = Stream.new(@model)
|
99
|
+
end
|
100
|
+
end
|
89
101
|
end
|
data/lib/redlander/node.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
module Redlander
|
2
|
-
|
3
2
|
class Node
|
4
|
-
|
5
3
|
attr_reader :rdf_node
|
6
4
|
|
7
5
|
# Create a RDF node.
|
@@ -9,58 +7,72 @@ module Redlander
|
|
9
7
|
# - an instance of URI - to create a RDF "resource",
|
10
8
|
# - an instance of Node - to create a copy of the node,
|
11
9
|
# - nil (or absent) - to create a "blank" node,
|
10
|
+
# - an instance of Statement ("role" must be supplied then) -
|
11
|
+
# to create a node from subject, predicate or object
|
12
|
+
# (determined by "role" parameter) of the statement.
|
12
13
|
# - any other Ruby object, which can be coerced into a literal.
|
13
14
|
# If nothing else, a RedlandError is thrown.
|
14
15
|
#
|
15
16
|
# Note, that you cannot create a resource node from an URI string,
|
16
17
|
# it must be an instance of URI. Otherwise it is treated as a string literal.
|
17
|
-
def initialize(arg = nil)
|
18
|
-
|
18
|
+
def initialize(arg = nil, role = :subject)
|
19
|
+
bound = false
|
20
|
+
@rdf_node = case arg
|
21
|
+
when NilClass
|
19
22
|
Redland.librdf_new_node_from_blank_identifier(Redlander.rdf_world, nil)
|
20
|
-
|
23
|
+
when URI
|
21
24
|
Redland.librdf_new_node_from_uri_string(Redlander.rdf_world, arg.to_s)
|
22
|
-
|
23
|
-
# A special case, where you can pass an instance of SWIG::TYPE_p_librdf_node_s
|
24
|
-
# in order to create a Node from an internal RDF node representation.
|
25
|
-
arg
|
26
|
-
elsif arg.is_a?(Node)
|
25
|
+
when Node
|
27
26
|
Redland.librdf_new_node_from_node(arg.rdf_node)
|
27
|
+
when Statement
|
28
|
+
bound = true
|
29
|
+
case role
|
30
|
+
when :subject
|
31
|
+
Redland.librdf_statement_get_subject(arg.rdf_statement)
|
32
|
+
when :object
|
33
|
+
Redland.librdf_statement_get_object(arg.rdf_statement)
|
34
|
+
when :predicate
|
35
|
+
Redland.librdf_statement_get_predicate(arg.rdf_statement)
|
36
|
+
else
|
37
|
+
raise RedlandError.new("Invalid role specified")
|
38
|
+
end
|
28
39
|
else
|
29
40
|
value = arg.respond_to?(:xmlschema) ? arg.xmlschema : arg.to_s
|
30
|
-
datatype =
|
31
|
-
Redland.librdf_new_node_from_typed_literal(Redlander.rdf_world, value, nil, datatype)
|
41
|
+
datatype = Uri.new(XmlSchema.datatype_of(arg))
|
42
|
+
Redland.librdf_new_node_from_typed_literal(Redlander.rdf_world, value, nil, datatype.rdf_uri)
|
32
43
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
44
|
+
if @rdf_node.null?
|
45
|
+
raise RedlandError.new("Failed to create a new node") unless bound
|
46
|
+
else
|
47
|
+
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_node(@rdf_node) })
|
48
|
+
# bound nodes cannot be added to (other) statements
|
49
|
+
freeze if bound
|
50
|
+
end
|
36
51
|
end
|
37
52
|
|
38
53
|
def resource?
|
39
|
-
|
54
|
+
Redland.librdf_node_is_resource(@rdf_node) != 0
|
40
55
|
end
|
41
56
|
|
42
57
|
# Return true if node is a literal.
|
43
58
|
def literal?
|
44
|
-
|
59
|
+
Redland.librdf_node_is_literal(@rdf_node) != 0
|
45
60
|
end
|
46
61
|
|
47
62
|
# Return true if node is a blank node.
|
48
63
|
def blank?
|
49
|
-
|
64
|
+
Redland.librdf_node_is_blank(@rdf_node) != 0
|
50
65
|
end
|
51
66
|
|
52
67
|
# Return the datatype URI of the node.
|
53
68
|
# Returns nil if the node is not a literal, or has no datatype URI.
|
54
69
|
def datatype
|
55
|
-
|
56
|
-
ObjectSpace.define_finalizer(rdf_uri, proc { Redland.librdf_free_uri(rdf_uri) })
|
57
|
-
Redland.librdf_uri_to_string(rdf_uri)
|
58
|
-
end
|
70
|
+
Uri.new(self).to_s if literal?
|
59
71
|
end
|
60
72
|
|
61
73
|
# Equivalency. Only works for comparing two Nodes.
|
62
74
|
def eql?(other_node)
|
63
|
-
|
75
|
+
Redland.librdf_node_equals(@rdf_node, other_node.rdf_node) != 0
|
64
76
|
end
|
65
77
|
alias_method :==, :eql?
|
66
78
|
|
@@ -76,15 +88,10 @@ module Redlander
|
|
76
88
|
# Value of the literal node as a Ruby object instance.
|
77
89
|
def value
|
78
90
|
if resource?
|
79
|
-
|
80
|
-
ObjectSpace.define_finalizer(rdf_uri, proc { Redland.librdf_free_uri(rdf_uri) })
|
81
|
-
URI.parse(Redland.librdf_uri_to_string(rdf_uri))
|
82
|
-
end
|
91
|
+
URI.parse(Uri.new(self).to_s)
|
83
92
|
else
|
84
93
|
XmlSchema.instantiate(to_s)
|
85
94
|
end
|
86
95
|
end
|
87
|
-
|
88
96
|
end
|
89
|
-
|
90
97
|
end
|
data/lib/redlander/parser.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
|
-
require 'redlander/parser_proxy'
|
2
|
-
|
3
1
|
module Redlander
|
4
|
-
|
5
2
|
class Parser
|
6
|
-
|
7
3
|
attr_reader :rdf_parser
|
8
4
|
|
9
5
|
# Create a new parser.
|
@@ -15,8 +11,8 @@ module Redlander
|
|
15
11
|
def initialize(name = :rdfxml)
|
16
12
|
# name, mime-type and syntax uri can all be nil, which defaults to :rdfxml parser
|
17
13
|
@rdf_parser = Redland.librdf_new_parser(Redlander.rdf_world, name.to_s, nil, nil)
|
18
|
-
raise RedlandError.new("Failed to create a new parser")
|
19
|
-
ObjectSpace.define_finalizer(
|
14
|
+
raise RedlandError.new("Failed to create a new parser") if @rdf_parser.null?
|
15
|
+
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_parser(@rdf_parser) })
|
20
16
|
end
|
21
17
|
|
22
18
|
# Parse the content (String) into the model.
|
@@ -28,7 +24,12 @@ module Redlander
|
|
28
24
|
def from_string(model, content, options = {})
|
29
25
|
# FIXME: A bug (?) in Redland breaks NTriples parser if its input is not terminated with "\n"
|
30
26
|
content.concat("\n") unless content.end_with?("\n")
|
31
|
-
|
27
|
+
base_uri = if options.has_key?(:base_uri)
|
28
|
+
Uri.new(options[:base_uri]).rdf_uri
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
Redland.librdf_parser_parse_string_into_model(@rdf_parser, content, base_uri, model.rdf_model).zero?
|
32
33
|
end
|
33
34
|
|
34
35
|
# Parse the content from URI into the model.
|
@@ -41,25 +42,24 @@ module Redlander
|
|
41
42
|
def from_uri(model, uri, options = {})
|
42
43
|
uri = URI.parse(uri)
|
43
44
|
uri = URI.parse("file://#{File.expand_path(uri.to_s)}") if uri.scheme.nil?
|
44
|
-
|
45
|
+
base_uri = if options.has_key?(:base_uri)
|
46
|
+
Uri.new(options[:base_uri]).rdf_uri
|
47
|
+
else
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
Redland.librdf_parser_parse_into_model(@rdf_parser, Uri.new(uri).rdf_uri, base_uri, model.rdf_model).zero?
|
45
51
|
end
|
46
52
|
alias_method :from_file, :from_uri
|
47
53
|
|
48
54
|
def statements(content, options = {})
|
49
|
-
# BUG: Parser accumulates data from consequent runs??? WTF, Redland?!
|
50
|
-
# When parsing a series of files, parser reported a duplicate entry,
|
51
|
-
# then seemed to have stopped yielding statements at all.
|
52
|
-
|
53
55
|
# FIXME: A bug (?) in Redland breaks NTriples parser if its input is not terminated with "\n"
|
54
56
|
content.concat("\n") unless content.end_with?("\n")
|
55
57
|
ParserProxy.new(self, content, options)
|
56
58
|
end
|
57
|
-
|
58
59
|
end
|
59
60
|
|
60
|
-
|
61
|
+
# Applied to Model
|
61
62
|
module ParsingInstanceMethods
|
62
|
-
|
63
63
|
def from_rdfxml(content, options = {})
|
64
64
|
parser = Parser.new(:rdfxml)
|
65
65
|
parser.from_string(self, content, options)
|
@@ -86,7 +86,5 @@ module Redlander
|
|
86
86
|
parser.from_uri(self, uri, parser_options)
|
87
87
|
end
|
88
88
|
alias_method :from_file, :from_uri
|
89
|
-
|
90
89
|
end
|
91
|
-
|
92
90
|
end
|