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
@@ -1,18 +1,19 @@
|
|
1
|
-
require 'redlander/statement'
|
2
|
-
|
3
1
|
module Redlander
|
4
|
-
|
5
2
|
class ParserProxy
|
6
|
-
|
7
|
-
include StatementIterator
|
3
|
+
include StreamEnumerator
|
8
4
|
|
9
5
|
def initialize(parser, content, options = {})
|
10
|
-
|
11
|
-
@
|
12
|
-
|
13
|
-
|
6
|
+
# TODO: consider a streaming content, as it may be large to fit in memory
|
7
|
+
@parser = parser
|
8
|
+
@content = content
|
9
|
+
@options = options
|
14
10
|
end
|
15
11
|
|
16
|
-
end
|
17
12
|
|
13
|
+
private
|
14
|
+
|
15
|
+
def reset_stream
|
16
|
+
@stream = Stream.new(@parser, @content, @options)
|
17
|
+
end
|
18
|
+
end
|
18
19
|
end
|
data/lib/redlander/serializer.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
module Redlander
|
2
|
-
|
3
2
|
class Serializer
|
4
|
-
|
5
3
|
# Create a new serializer.
|
6
4
|
# Name can be either of [:rdfxml, :ntriples, :turtle, :json, :dot],
|
7
5
|
# or nil, which defaults to :rdfxml.
|
@@ -10,8 +8,8 @@ module Redlander
|
|
10
8
|
# because the rest seem to be very buggy.
|
11
9
|
def initialize(name = :rdfxml)
|
12
10
|
@rdf_serializer = Redland.librdf_new_serializer(Redlander.rdf_world, name.to_s, nil, nil)
|
13
|
-
raise RedlandError.new("Failed to create a new serializer")
|
14
|
-
ObjectSpace.define_finalizer(
|
11
|
+
raise RedlandError.new("Failed to create a new serializer") if @rdf_serializer.null?
|
12
|
+
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_serializer(@rdf_serializer) })
|
15
13
|
end
|
16
14
|
|
17
15
|
# Serialize a model into a string.
|
@@ -19,7 +17,12 @@ module Redlander
|
|
19
17
|
# Options are:
|
20
18
|
# :base_uri base URI (String or URI)
|
21
19
|
def to_string(model, options = {})
|
22
|
-
|
20
|
+
base_uri = if options.has_key?(:base_uri)
|
21
|
+
Uri.new(options[:base_uri]).rdf_uri
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
Redland.librdf_serializer_serialize_model_to_string(@rdf_serializer, base_uri, model.rdf_model)
|
23
26
|
end
|
24
27
|
|
25
28
|
# Serializes a model and stores it in a file
|
@@ -31,14 +34,18 @@ module Redlander
|
|
31
34
|
#
|
32
35
|
# Returns true on success, or false.
|
33
36
|
def to_file(model, filename, options = {})
|
34
|
-
|
37
|
+
base_uri = if options.has_key?(:base_uri)
|
38
|
+
Uri.new(options[:base_uri]).rdf_uri
|
39
|
+
else
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
Redland.librdf_serializer_serialize_model_to_file(@rdf_serializer, filename, base_uri, model.rdf_model).zero?
|
35
43
|
end
|
36
|
-
|
37
44
|
end
|
38
45
|
|
39
46
|
|
47
|
+
# Applied to Model
|
40
48
|
module SerializingInstanceMethods
|
41
|
-
|
42
49
|
def to_rdfxml(options = {})
|
43
50
|
serializer = Serializer.new(:rdfxml)
|
44
51
|
serializer.to_string(self, options)
|
@@ -74,7 +81,5 @@ module Redlander
|
|
74
81
|
serializer = Serializer.new(serializer_options.delete(:format) || :rdfxml)
|
75
82
|
serializer.to_file(self, filename, serializer_options)
|
76
83
|
end
|
77
|
-
|
78
84
|
end
|
79
|
-
|
80
85
|
end
|
data/lib/redlander/statement.rb
CHANGED
@@ -1,105 +1,73 @@
|
|
1
1
|
module Redlander
|
2
|
-
|
3
2
|
class Statement
|
4
|
-
|
5
3
|
include ErrorContainer
|
6
4
|
|
7
5
|
attr_reader :rdf_statement
|
8
6
|
|
9
7
|
# Create an RDF statement.
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
8
|
+
# Source can be:
|
9
|
+
# Hash, where
|
10
|
+
# :subject
|
11
|
+
# :predicate
|
12
|
+
# :object
|
13
|
+
# Stream, so that a statement is extracted from its current position
|
14
|
+
def initialize(source = {})
|
15
|
+
@rdf_statement = case source
|
16
|
+
when Stream
|
17
|
+
# Pull a (current) statement from the stream
|
18
|
+
Redland.librdf_stream_get_object(source.rdf_stream)
|
19
|
+
when Hash
|
20
|
+
# Create a new statement from nodes
|
21
|
+
s = source[:subject] && Node.new(source[:subject]).rdf_node
|
22
|
+
p = source[:predicate] && Node.new(source[:predicate]).rdf_node
|
23
|
+
o = source[:object] && Node.new(source[:object]).rdf_node
|
23
24
|
Redland.librdf_new_statement_from_nodes(Redlander.rdf_world, s, p, o)
|
25
|
+
else
|
26
|
+
# TODO
|
27
|
+
raise NotImplementedError.new
|
24
28
|
end
|
25
|
-
|
26
|
-
|
27
|
-
ObjectSpace.define_finalizer(@rdf_statement, proc { Redland.librdf_free_statement(@rdf_statement) })
|
29
|
+
raise RedlandError.new("Failed to create a new statement") if @rdf_statement.null?
|
30
|
+
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_statement(@rdf_statement) })
|
28
31
|
end
|
29
32
|
|
30
33
|
def subject
|
31
|
-
|
32
|
-
rdf_node
|
34
|
+
node = Node.new(self, :subject)
|
35
|
+
node.rdf_node.null? ? nil : node
|
33
36
|
end
|
34
37
|
|
35
38
|
def predicate
|
36
|
-
|
37
|
-
rdf_node
|
39
|
+
node = Node.new(self, :predicate)
|
40
|
+
node.rdf_node.null? ? nil : node
|
38
41
|
end
|
39
42
|
|
40
43
|
def object
|
41
|
-
|
42
|
-
rdf_node
|
44
|
+
node = Node.new(self, :object)
|
45
|
+
node.rdf_node.null? ? nil : node
|
43
46
|
end
|
44
47
|
|
45
48
|
# set the subject of the statement
|
46
49
|
def subject=(node)
|
47
|
-
|
50
|
+
binding_to_statement(node) {
|
51
|
+
Redland.librdf_statement_set_subject(@rdf_statement, node.rdf_node)
|
52
|
+
}
|
48
53
|
end
|
49
54
|
|
50
55
|
# set the predicate of the statement
|
51
56
|
def predicate=(node)
|
52
|
-
|
57
|
+
binding_to_statement(node) {
|
58
|
+
Redland.librdf_statement_set_predicate(@rdf_statement, node.rdf_node)
|
59
|
+
}
|
53
60
|
end
|
54
61
|
|
55
62
|
# set the object of the statement
|
56
63
|
def object=(node)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
def model
|
61
|
-
@model
|
62
|
-
end
|
63
|
-
|
64
|
-
# Add the statement to the given model.
|
65
|
-
#
|
66
|
-
# Returns the model on success, or nil.
|
67
|
-
# NOTE: Duplicate statements are not added to the model.
|
68
|
-
# However, this doesn't result in an error here.
|
69
|
-
def model=(model)
|
70
|
-
if model.nil?
|
71
|
-
@model = nil
|
72
|
-
else
|
73
|
-
if self.valid?
|
74
|
-
if Redland.librdf_model_add_statement(model.rdf_model, @rdf_statement).zero?
|
75
|
-
@model = model
|
76
|
-
else
|
77
|
-
nil
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# Destroy the statement (remove it from the model, if possible).
|
84
|
-
#
|
85
|
-
# Returns true if successfully removed from the model, or false.
|
86
|
-
# If the statement is not bound to a model, false is returned.
|
87
|
-
def destroy
|
88
|
-
if @model
|
89
|
-
if Redland.librdf_model_remove_statement(@model.rdf_model, @rdf_statement).zero?
|
90
|
-
self.model = nil
|
91
|
-
true
|
92
|
-
else
|
93
|
-
false
|
94
|
-
end
|
95
|
-
else
|
96
|
-
false
|
97
|
-
end
|
64
|
+
binding_to_statement(node) {
|
65
|
+
Redland.librdf_statement_set_object(@rdf_statement, node.rdf_node)
|
66
|
+
}
|
98
67
|
end
|
99
68
|
|
100
69
|
def eql?(other_statement)
|
101
|
-
|
102
|
-
subject == other_statement.subject &&
|
70
|
+
subject == other_statement.subject &&
|
103
71
|
predicate == other_statement.predicate &&
|
104
72
|
object == other_statement.object
|
105
73
|
end
|
@@ -116,38 +84,26 @@ module Redlander
|
|
116
84
|
# A valid statement satisfies the following:
|
117
85
|
# URI or blank subject, URI predicate and URI or blank or literal object (i.e. anything).
|
118
86
|
def valid?
|
119
|
-
|
120
|
-
|
121
|
-
else
|
122
|
-
errors.add("is invalid")
|
123
|
-
end
|
124
|
-
is_valid
|
87
|
+
attributes_satisfy? ? errors.clear : errors.add("is invalid")
|
88
|
+
errors.empty?
|
125
89
|
end
|
126
90
|
|
127
91
|
|
128
92
|
private
|
129
93
|
|
130
|
-
def rdf_node_from(node)
|
131
|
-
if node.nil?
|
132
|
-
nil
|
133
|
-
else
|
134
|
-
# According to Redland docs,
|
135
|
-
# the node here becomes a part of the statement
|
136
|
-
# and must not be used by the caller!
|
137
|
-
if node.frozen?
|
138
|
-
raise RedlandError.new("The node is already bound to a statement and cannot be added.")
|
139
|
-
else
|
140
|
-
node.freeze.rdf_node
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
94
|
def attributes_satisfy?
|
146
95
|
!subject.nil? && (subject.resource? || subject.blank?) &&
|
147
96
|
!predicate.nil? && predicate.resource? &&
|
148
97
|
!object.nil?
|
149
98
|
end
|
150
99
|
|
100
|
+
def binding_to_statement(node)
|
101
|
+
if node.frozen?
|
102
|
+
raise RedlandError.new("Cannot assign a bound node")
|
103
|
+
else
|
104
|
+
node.freeze
|
105
|
+
yield
|
106
|
+
end
|
107
|
+
end
|
151
108
|
end
|
152
|
-
|
153
109
|
end
|
data/lib/redlander/storage.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Redlander
|
2
|
-
|
3
|
-
module Storage
|
4
|
-
|
2
|
+
class Storage
|
5
3
|
VALID_STORAGE_TYPES = [:memory, :hashes, :file, :uri, :tstore, :mysql, :sqlite, :postgresql]
|
6
4
|
|
5
|
+
attr_reader :rdf_storage
|
6
|
+
|
7
7
|
# Creates a store of the given type
|
8
8
|
#
|
9
9
|
# Store types (:storage option) are:
|
@@ -31,49 +31,27 @@ module Redlander
|
|
31
31
|
#
|
32
32
|
# NOTE: When dealing with databases,
|
33
33
|
# Redland (1.0.7) just crashes when the required tables aren't available!
|
34
|
-
def
|
35
|
-
storage_type, storage_options = split_options(options)
|
36
|
-
storage_type ||= :memory
|
34
|
+
def initialize(options = {})
|
35
|
+
storage_type, storage_options = split_options(options.dup)
|
37
36
|
|
38
37
|
unless VALID_STORAGE_TYPES.include?(storage_type)
|
39
38
|
raise RedlandError.new("Unknown storage type: #{storage_type}")
|
40
39
|
end
|
41
40
|
|
42
|
-
rdf_storage = Redland.librdf_new_storage(Redlander.rdf_world,
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
raise RedlandError.new("Failed to initialize storage")
|
47
|
-
ObjectSpace.define_finalizer(
|
48
|
-
|
49
|
-
rdf_storage
|
50
|
-
end
|
51
|
-
|
52
|
-
# Wrap changes to the given model in a transaction.
|
53
|
-
# If an exception is raised in the block, the transaction is rolled back.
|
54
|
-
# (Does not work for all storages, in which case the changes are instanteous).
|
55
|
-
def self.transaction(model, &block)
|
56
|
-
Redland.librdf_model_transaction_start(model.rdf_model).zero? || RedlandError.new("Failed to initialize a transaction")
|
57
|
-
block.call
|
58
|
-
Redland.librdf_model_transaction_commit(model.rdf_model).zero? || RedlandError.new("Failed to commit the transaction")
|
59
|
-
rescue
|
60
|
-
rollback(model)
|
61
|
-
end
|
62
|
-
|
63
|
-
# Rollback a latest transaction for the given model.
|
64
|
-
def self.rollback(model)
|
65
|
-
Redland.librdf_model_transaction_rollback(model.rdf_model).zero? || RedlandError.new("Failed to rollback the latest transaction")
|
41
|
+
@rdf_storage = Redland.librdf_new_storage(Redlander.rdf_world,
|
42
|
+
storage_type.to_s,
|
43
|
+
storage_options.delete(:name).to_s,
|
44
|
+
Redlander.to_rdf_options(storage_options))
|
45
|
+
raise RedlandError.new("Failed to initialize storage") if @rdf_storage.null?
|
46
|
+
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_storage(@rdf_storage) })
|
66
47
|
end
|
67
48
|
|
68
49
|
|
69
50
|
private
|
70
51
|
|
71
|
-
def
|
72
|
-
|
73
|
-
storage_type
|
74
|
-
[storage_type, storage_options]
|
52
|
+
def split_options(options)
|
53
|
+
storage_type = options.delete(:storage) || :memory
|
54
|
+
[storage_type, options]
|
75
55
|
end
|
76
|
-
|
77
56
|
end
|
78
|
-
|
79
57
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Redlander
|
2
|
+
class Stream
|
3
|
+
attr_reader :rdf_stream
|
4
|
+
|
5
|
+
# Convert something to an RDF stream.
|
6
|
+
# Source can be:
|
7
|
+
# Parser - to parse content into a stream
|
8
|
+
# Model - to convert a model to an RDF stream, or
|
9
|
+
# if content (Statement) supplied,
|
10
|
+
# produce a stream of statements from the given model,
|
11
|
+
# matching the non-empty nodes of the given statement.
|
12
|
+
def initialize(source, content = nil, options = {})
|
13
|
+
@rdf_stream = case source
|
14
|
+
when Model
|
15
|
+
if content.is_a?(Statement)
|
16
|
+
Redland.librdf_model_find_statements(source.rdf_model, content.rdf_statement)
|
17
|
+
else
|
18
|
+
Redland.librdf_model_as_stream(source.rdf_model)
|
19
|
+
end
|
20
|
+
when Parser
|
21
|
+
base_uri = options[:base_uri] ? Uri.new(options[:base_uri]).rdf_uri : nil
|
22
|
+
Redland.librdf_parser_parse_string_as_stream(source.rdf_parser, content, base_uri)
|
23
|
+
else
|
24
|
+
# TODO
|
25
|
+
raise NotImplementedError.new
|
26
|
+
end
|
27
|
+
raise RedlandError.new("Failed to create a new stream") if @rdf_stream.null?
|
28
|
+
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_stream(@rdf_stream) })
|
29
|
+
end
|
30
|
+
|
31
|
+
# End-of-stream?
|
32
|
+
def eos?
|
33
|
+
Redland.librdf_stream_end(@rdf_stream) != 0
|
34
|
+
end
|
35
|
+
|
36
|
+
# Move stream pointer forward
|
37
|
+
def succ
|
38
|
+
Redland.librdf_stream_next(@rdf_stream).zero?
|
39
|
+
end
|
40
|
+
|
41
|
+
# Current statement in the stream, or nil
|
42
|
+
def current
|
43
|
+
Statement.new(self) unless eos?
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return all the remaining statements in the stream
|
47
|
+
# from the current position.
|
48
|
+
def tail
|
49
|
+
[].tap do |all|
|
50
|
+
while !eos?
|
51
|
+
all << current
|
52
|
+
succ
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Redlander
|
2
|
+
class Stream
|
3
|
+
attr_reader :rdf_stream
|
4
|
+
|
5
|
+
def initialize(source)
|
6
|
+
@rdf_stream = case source
|
7
|
+
when Statement
|
8
|
+
Redland.librdf_model_find_statements(source.model.rdf_model, source.rdf_statement)
|
9
|
+
when Model
|
10
|
+
Redland.librdf_model_as_stream(source.rdf_model)
|
11
|
+
else
|
12
|
+
# TODO
|
13
|
+
raise NotImplementedError.new
|
14
|
+
end
|
15
|
+
raise RedlandError.new("Failed to create a new stream") if @rdf_stream.null?
|
16
|
+
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_stream(@rdf_stream) })
|
17
|
+
end
|
18
|
+
|
19
|
+
# End-of-stream?
|
20
|
+
def eos?
|
21
|
+
Redland.librdf_stream_end(@rdf_stream) != 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def succ
|
25
|
+
Redland.librdf_stream_next(@rdf_stream).zero?
|
26
|
+
end
|
27
|
+
|
28
|
+
def current
|
29
|
+
Statement.new(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return all the remaining statements in the stream
|
33
|
+
# from the current position.
|
34
|
+
def tail
|
35
|
+
[].tap do |all|
|
36
|
+
while !eos?
|
37
|
+
all << current
|
38
|
+
succ
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|