redlander 0.2.2 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|