redlander 0.6.1 → 0.6.2
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.
- checksums.yaml +4 -4
- data/ChangeLog +4 -0
- data/README.md +21 -2
- data/lib/redlander.rb +11 -1
- data/lib/redlander/model.rb +16 -2
- data/lib/redlander/node.rb +55 -33
- data/lib/redlander/query/results.rb +11 -1
- data/lib/redlander/statement.rb +58 -27
- data/lib/redlander/uri.rb +27 -13
- data/lib/redlander/version.rb +1 -1
- data/spec/integration/finalizer_gc_spec.rb +82 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1adddb051e9cc3f1aeda6b6eaa46012b87647506
|
4
|
+
data.tar.gz: 0cf0890720062fbc51b5123d27837aeb0ce86d3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a0e2236aa7a13cb3d71c97b12d43d4079e41cbf9472731b26bc18e22f178970acb340c417f91c3c0e0fe8a0796e2ed6aa14f92c90d9aa0f7f7ebeb3424807be
|
7
|
+
data.tar.gz: d5cb579ca3f38910c6346f7706c0ddd296695c22626559c323445b445fef5e75060095b0d36c120062e62970c7c8befa7fbd76fd41e6f2e60007055202450561
|
data/ChangeLog
CHANGED
data/README.md
CHANGED
@@ -5,6 +5,7 @@ which is used to manipulate RDF graphs. This is an alternative implementation
|
|
5
5
|
of Ruby bindings (as opposed to the official bindings), aiming to be more
|
6
6
|
intuitive, lightweight, high-performing and as bug-free as possible.
|
7
7
|
|
8
|
+
|
8
9
|
# Installing
|
9
10
|
|
10
11
|
Installing Redlander is simple:
|
@@ -13,6 +14,7 @@ Installing Redlander is simple:
|
|
13
14
|
|
14
15
|
Note, that you will have to install Redland runtime library (librdf) for Redlander to work.
|
15
16
|
|
17
|
+
|
16
18
|
# Usage
|
17
19
|
|
18
20
|
This README outlines most obvious use cases.
|
@@ -29,6 +31,7 @@ for the list of available options.
|
|
29
31
|
Naturally, you don't need to create a model if you just want to play around
|
30
32
|
with independent statements, nodes and the like.
|
31
33
|
|
34
|
+
|
32
35
|
## RDF Statements
|
33
36
|
|
34
37
|
Now that you have created a model, you can access its RDF statements:
|
@@ -52,7 +55,8 @@ The API is almost identical to [ActiveRecord](https://github.com/rails/rails/tre
|
|
52
55
|
|
53
56
|
$ m.statements.each { |st| puts st }
|
54
57
|
|
55
|
-
|
58
|
+
|
59
|
+
### Finding and enumerating statements
|
56
60
|
|
57
61
|
$ m.statements.find(:first, :object => "subject!")
|
58
62
|
$ m.statements.all(:object => "another label")
|
@@ -60,7 +64,20 @@ Finding statements:
|
|
60
64
|
puts statement.subject
|
61
65
|
}
|
62
66
|
|
63
|
-
Note that `m.statements.each`
|
67
|
+
Note that `m.statements.each` does not have to pull and instantiate all statements in one call,
|
68
|
+
while `m.statements.all` (and other finders) can potentially create huge arrays of data
|
69
|
+
before you can handle individual statements of it.
|
70
|
+
|
71
|
+
For those interested in laziness, `m.statements` has `lazy` method which works exactly as users of
|
72
|
+
Ruby 2+ would expect:
|
73
|
+
|
74
|
+
$ m.statements.lazy.each {|s| puts s.object }
|
75
|
+
|
76
|
+
This, and other similar features are inherited by `m.statements` (which is actually an instance of
|
77
|
+
`Redlander::ModelProxy`) from `Enumerable` module.
|
78
|
+
|
79
|
+
|
80
|
+
### Accessing and querying subject, predicate and object
|
64
81
|
|
65
82
|
You can access the subject, predicate or object of a statement:
|
66
83
|
|
@@ -82,6 +99,7 @@ hash for *SELECT* queries. Binding hash values are instances of `Redlander::Node
|
|
82
99
|
|
83
100
|
For query options and available query languages refer to `Model#query` documentation.
|
84
101
|
|
102
|
+
|
85
103
|
### Localized string literals
|
86
104
|
|
87
105
|
Localized string literals are instantiated as LocalizedString objects.
|
@@ -191,6 +209,7 @@ SPARQL DESCRIBE is not implemented in librdf.
|
|
191
209
|
# Authors and Contributors
|
192
210
|
|
193
211
|
[Slava Kravchenko](https://github.com/cordawyn)
|
212
|
+
[Anthony Bargnesi](https://github.com/abargnesi)
|
194
213
|
|
195
214
|
|
196
215
|
# Thanks
|
data/lib/redlander.rb
CHANGED
@@ -12,17 +12,19 @@ require 'redlander/statement'
|
|
12
12
|
# Main Redlander namespace
|
13
13
|
module Redlander
|
14
14
|
class << self
|
15
|
+
|
15
16
|
# @api private
|
16
17
|
def rdf_world
|
17
18
|
unless @rdf_world
|
18
19
|
@rdf_world = Redland.librdf_new_world
|
19
20
|
raise RedlandError, "Could not create a new RDF world" if @rdf_world.null?
|
20
|
-
ObjectSpace.define_finalizer(self,
|
21
|
+
ObjectSpace.define_finalizer(self, finalize_world(@rdf_world))
|
21
22
|
Redland.librdf_world_open(@rdf_world)
|
22
23
|
end
|
23
24
|
@rdf_world
|
24
25
|
end
|
25
26
|
|
27
|
+
|
26
28
|
# @api private
|
27
29
|
# Convert options hash into a string for librdf.
|
28
30
|
# What it does:
|
@@ -44,5 +46,13 @@ module Redlander
|
|
44
46
|
opts << "#{key}='#{value}'"
|
45
47
|
}.join(',')
|
46
48
|
end
|
49
|
+
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# @api private
|
54
|
+
def finalize_world(rdf_world_ptr)
|
55
|
+
proc { Redland.librdf_free_world(rdf_world_ptr) }
|
56
|
+
end
|
47
57
|
end
|
48
58
|
end
|
data/lib/redlander/model.rb
CHANGED
@@ -12,6 +12,19 @@ module Redlander
|
|
12
12
|
# @api private
|
13
13
|
attr_reader :rdf_model
|
14
14
|
|
15
|
+
class << self
|
16
|
+
private
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
def finalize_storage(rdf_storage_ptr)
|
20
|
+
proc { Redland.librdf_free_storage(rdf_storage_ptr) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def finalize_model(rdf_model_ptr)
|
24
|
+
proc { Redland.librdf_free_model(rdf_model_ptr) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
15
28
|
# Create a new RDF model.
|
16
29
|
# (For available storage options see http://librdf.org/docs/api/redland-storage-modules.html)
|
17
30
|
#
|
@@ -50,11 +63,12 @@ module Redlander
|
|
50
63
|
storage_name.to_s,
|
51
64
|
Redlander.to_rdf_options(options))
|
52
65
|
raise RedlandError, "Failed to initialize '#{storage_name}' storage (type: #{storage_type})" if @rdf_storage.null?
|
53
|
-
ObjectSpace.define_finalizer(self,
|
66
|
+
ObjectSpace.define_finalizer(self, self.class.send(:finalize_storage, @rdf_storage))
|
54
67
|
|
55
68
|
@rdf_model = Redland.librdf_new_model(Redlander.rdf_world, @rdf_storage, "")
|
56
69
|
raise RedlandError, "Failed to create a new model" if @rdf_model.null?
|
57
|
-
|
70
|
+
|
71
|
+
ObjectSpace.define_finalizer(self, self.class.send(:finalize_model, @rdf_model))
|
58
72
|
end
|
59
73
|
|
60
74
|
# Statements contained in the model.
|
data/lib/redlander/node.rb
CHANGED
@@ -1,11 +1,50 @@
|
|
1
1
|
module Redlander
|
2
2
|
# RDF node (usually, a part of an RDF statement)
|
3
3
|
class Node
|
4
|
+
class << self
|
5
|
+
private
|
6
|
+
|
7
|
+
# @api private
|
8
|
+
def finalize_node(rdf_node_ptr)
|
9
|
+
proc { Redland.librdf_free_node(rdf_node_ptr) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
4
13
|
# @api private
|
5
|
-
|
14
|
+
def rdf_node
|
15
|
+
unless instance_variable_defined?(:@rdf_node)
|
16
|
+
@rdf_node = case @arg
|
17
|
+
when FFI::Pointer
|
18
|
+
@arg
|
19
|
+
when NilClass
|
20
|
+
Redland.librdf_new_node_from_blank_identifier(Redlander.rdf_world, @options[:blank_id])
|
21
|
+
when URI
|
22
|
+
Redland.librdf_new_node_from_uri_string(Redlander.rdf_world, @arg.to_s)
|
23
|
+
else
|
24
|
+
value = @arg.respond_to?(:xmlschema) ? @arg.xmlschema : @arg.to_s
|
25
|
+
lang = @arg.respond_to?(:lang) ? @arg.lang.to_s : nil
|
26
|
+
dt = lang ? nil : Uri.new(XmlSchema.datatype_of(@arg)).rdf_uri
|
27
|
+
Redland.librdf_new_node_from_typed_literal(Redlander.rdf_world, value, lang, dt)
|
28
|
+
end
|
29
|
+
raise RedlandError, "Failed to create a new node" if @rdf_node.null?
|
30
|
+
ObjectSpace.define_finalizer(self, self.class.send(:finalize_node, @rdf_node))
|
31
|
+
end
|
32
|
+
@rdf_node
|
33
|
+
end
|
6
34
|
|
7
35
|
# Datatype URI for the literal node, or nil
|
8
|
-
|
36
|
+
def datatype
|
37
|
+
if instance_variable_defined?(:@datatype)
|
38
|
+
@datatype
|
39
|
+
else
|
40
|
+
@datatype = if literal?
|
41
|
+
rdf_uri = Redland.librdf_node_get_literal_value_datatype_uri(rdf_node)
|
42
|
+
rdf_uri.null? ? XmlSchema.datatype_of("") : URI.parse(Redland.librdf_uri_to_string(rdf_uri))
|
43
|
+
else
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
9
48
|
|
10
49
|
# Create a RDF node.
|
11
50
|
#
|
@@ -19,47 +58,31 @@ module Redlander
|
|
19
58
|
# @option options [String] :blank_id optional ID to use for a blank node.
|
20
59
|
# @raise [RedlandError] if it fails to create a node from the given args.
|
21
60
|
def initialize(arg = nil, options = {})
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@datatype = rdf_uri.null? ? XmlSchema.datatype_of("") : URI(Redland.librdf_uri_to_string(rdf_uri))
|
27
|
-
end
|
28
|
-
wrap(arg)
|
29
|
-
when NilClass
|
30
|
-
Redland.librdf_new_node_from_blank_identifier(Redlander.rdf_world, options[:blank_id])
|
31
|
-
when URI
|
32
|
-
Redland.librdf_new_node_from_uri_string(Redlander.rdf_world, arg.to_s)
|
33
|
-
else
|
34
|
-
@datatype = XmlSchema.datatype_of(arg)
|
35
|
-
value = arg.respond_to?(:xmlschema) ? arg.xmlschema : arg.to_s
|
36
|
-
lang = arg.respond_to?(:lang) ? arg.lang.to_s : nil
|
37
|
-
dt = lang ? nil : Uri.new(@datatype).rdf_uri
|
38
|
-
Redland.librdf_new_node_from_typed_literal(Redlander.rdf_world, value, lang, dt)
|
39
|
-
end
|
40
|
-
raise RedlandError, "Failed to create a new node" if @rdf_node.null?
|
41
|
-
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_node(@rdf_node) })
|
61
|
+
# If FFI::Pointer is passed, wrap it instantly,
|
62
|
+
# because it can be freed outside before it is used here.
|
63
|
+
@arg = arg.is_a?(FFI::Pointer) ? wrap(arg) : arg
|
64
|
+
@options = options
|
42
65
|
end
|
43
66
|
|
44
67
|
# Check whether the node is a resource (identified by a URI)
|
45
68
|
#
|
46
69
|
# @return [Boolean]
|
47
70
|
def resource?
|
48
|
-
Redland.librdf_node_is_resource(
|
71
|
+
Redland.librdf_node_is_resource(rdf_node) != 0
|
49
72
|
end
|
50
73
|
|
51
74
|
# Return true if node is a literal.
|
52
75
|
#
|
53
76
|
# @return [Boolean]
|
54
77
|
def literal?
|
55
|
-
Redland.librdf_node_is_literal(
|
78
|
+
Redland.librdf_node_is_literal(rdf_node) != 0
|
56
79
|
end
|
57
80
|
|
58
81
|
# Return true if node is a blank node.
|
59
82
|
#
|
60
83
|
# @return [Boolean]
|
61
84
|
def blank?
|
62
|
-
Redland.librdf_node_is_blank(
|
85
|
+
Redland.librdf_node_is_blank(rdf_node) != 0
|
63
86
|
end
|
64
87
|
|
65
88
|
# Equivalency. Only works for comparing two Nodes.
|
@@ -67,7 +90,7 @@ module Redlander
|
|
67
90
|
# @param [Node] other_node Node to be compared with.
|
68
91
|
# @return [Boolean]
|
69
92
|
def eql?(other_node)
|
70
|
-
Redland.librdf_node_equals(
|
93
|
+
Redland.librdf_node_equals(rdf_node, other_node.rdf_node) != 0
|
71
94
|
end
|
72
95
|
alias_method :==, :eql?
|
73
96
|
|
@@ -79,7 +102,7 @@ module Redlander
|
|
79
102
|
#
|
80
103
|
# @return [String]
|
81
104
|
def to_s
|
82
|
-
Redland.librdf_node_to_string(
|
105
|
+
Redland.librdf_node_to_string(rdf_node)
|
83
106
|
end
|
84
107
|
|
85
108
|
# Internal URI of the Node.
|
@@ -90,7 +113,7 @@ module Redlander
|
|
90
113
|
# @return [URI, nil]
|
91
114
|
def uri
|
92
115
|
if resource?
|
93
|
-
URI(to_s[1..-2])
|
116
|
+
URI.parse(to_s[1..-2])
|
94
117
|
elsif literal?
|
95
118
|
datatype
|
96
119
|
else
|
@@ -108,20 +131,19 @@ module Redlander
|
|
108
131
|
if resource?
|
109
132
|
uri
|
110
133
|
elsif blank?
|
111
|
-
Redland.librdf_node_get_blank_identifier(
|
134
|
+
Redland.librdf_node_get_blank_identifier(rdf_node).force_encoding("UTF-8")
|
112
135
|
else
|
113
|
-
v = Redland.librdf_node_get_literal_value(
|
136
|
+
v = Redland.librdf_node_get_literal_value(rdf_node).force_encoding("UTF-8")
|
114
137
|
v << "@#{lang}" if lang
|
115
|
-
XmlSchema.instantiate(v,
|
138
|
+
XmlSchema.instantiate(v, datatype)
|
116
139
|
end
|
117
140
|
end
|
118
141
|
|
119
142
|
def lang
|
120
|
-
lng = Redland.librdf_node_get_literal_value_language(
|
143
|
+
lng = Redland.librdf_node_get_literal_value_language(rdf_node)
|
121
144
|
lng ? lng.to_sym : nil
|
122
145
|
end
|
123
146
|
|
124
|
-
|
125
147
|
private
|
126
148
|
|
127
149
|
# @api private
|
@@ -4,6 +4,15 @@ module Redlander
|
|
4
4
|
class Results
|
5
5
|
include Enumerable
|
6
6
|
|
7
|
+
class << self
|
8
|
+
private
|
9
|
+
|
10
|
+
# @api private
|
11
|
+
def finalize_query(rdf_query_ptr)
|
12
|
+
proc { Redland.librdf_free_query(rdf_query_ptr) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
7
16
|
# (see Model#query)
|
8
17
|
def initialize(q, options = {})
|
9
18
|
language = options[:language] || "sparql10"
|
@@ -13,7 +22,7 @@ module Redlander
|
|
13
22
|
@rdf_query = Redland.librdf_new_query(Redlander.rdf_world, language, language_uri, q, base_uri)
|
14
23
|
raise RedlandError, "Failed to create a #{language.upcase} query from '#{q}'" if @rdf_query.null?
|
15
24
|
|
16
|
-
ObjectSpace.define_finalizer(self,
|
25
|
+
ObjectSpace.define_finalizer(self, self.class.send(:finalize_query, @rdf_query))
|
17
26
|
end
|
18
27
|
|
19
28
|
def process(model)
|
@@ -76,6 +85,7 @@ module Redlander
|
|
76
85
|
!Redland.librdf_query_results_is_syntax(@rdf_results).zero?
|
77
86
|
end
|
78
87
|
|
88
|
+
|
79
89
|
private
|
80
90
|
|
81
91
|
def process_bindings
|
data/lib/redlander/statement.rb
CHANGED
@@ -1,8 +1,35 @@
|
|
1
1
|
module Redlander
|
2
2
|
# RDF statement
|
3
3
|
class Statement
|
4
|
+
class << self
|
5
|
+
private
|
6
|
+
|
7
|
+
# @api private
|
8
|
+
def finalize_statement(rdf_statement_ptr)
|
9
|
+
proc { Redland.librdf_free_statement(rdf_statement_ptr) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
4
13
|
# @api private
|
5
|
-
|
14
|
+
def rdf_statement
|
15
|
+
unless instance_variable_defined?(:@rdf_statement)
|
16
|
+
@rdf_statement = case @source
|
17
|
+
when FFI::Pointer
|
18
|
+
@source
|
19
|
+
when Hash
|
20
|
+
# Create a new statement from nodes
|
21
|
+
s = rdf_node_from(@source[:subject])
|
22
|
+
p = rdf_node_from(@source[:predicate])
|
23
|
+
o = rdf_node_from(@source[:object])
|
24
|
+
Redland.librdf_new_statement_from_nodes(Redlander.rdf_world, s, p, o)
|
25
|
+
else
|
26
|
+
raise NotImplementedError, "Cannot create Statement from '#{@source.inspect}'"
|
27
|
+
end
|
28
|
+
raise RedlandError, "Failed to create a new statement" if @rdf_statement.null?
|
29
|
+
ObjectSpace.define_finalizer(self, self.class.send(:finalize_statement, @rdf_statement))
|
30
|
+
end
|
31
|
+
@rdf_statement
|
32
|
+
end
|
6
33
|
|
7
34
|
# Create an RDF statement.
|
8
35
|
#
|
@@ -13,44 +40,45 @@ module Redlander
|
|
13
40
|
# @raise [NotImplementedError] if cannot create a Statement from the given source.
|
14
41
|
# @raise [RedlandError] if it fails to create a Statement.
|
15
42
|
def initialize(source = {})
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
when Hash
|
20
|
-
# Create a new statement from nodes
|
21
|
-
s = rdf_node_from(source[:subject])
|
22
|
-
p = rdf_node_from(source[:predicate])
|
23
|
-
o = rdf_node_from(source[:object])
|
24
|
-
Redland.librdf_new_statement_from_nodes(Redlander.rdf_world, s, p, o)
|
25
|
-
else
|
26
|
-
raise NotImplementedError, "Cannot create Statement from '#{source.inspect}'"
|
27
|
-
end
|
28
|
-
raise RedlandError, "Failed to create a new statement" if @rdf_statement.null?
|
29
|
-
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_statement(@rdf_statement) })
|
43
|
+
# If FFI::Pointer is passed, wrap it instantly,
|
44
|
+
# because it can be freed outside before it is used here.
|
45
|
+
@source = source.is_a?(FFI::Pointer) ? wrap(source) : source
|
30
46
|
end
|
31
47
|
|
32
48
|
# Subject of the statment.
|
33
49
|
#
|
34
50
|
# @return [Node, nil]
|
35
51
|
def subject
|
36
|
-
|
37
|
-
|
52
|
+
if instance_variable_defined?(:@subject)
|
53
|
+
@subject
|
54
|
+
else
|
55
|
+
rdf_node = Redland.librdf_statement_get_subject(rdf_statement)
|
56
|
+
@subject = rdf_node.null? ? nil : Node.new(rdf_node)
|
57
|
+
end
|
38
58
|
end
|
39
59
|
|
40
60
|
# Predicate of the statement.
|
41
61
|
#
|
42
62
|
# @return [Node, nil]
|
43
63
|
def predicate
|
44
|
-
|
45
|
-
|
64
|
+
if instance_variable_defined?(:@predicate)
|
65
|
+
@predicate
|
66
|
+
else
|
67
|
+
rdf_node = Redland.librdf_statement_get_predicate(rdf_statement)
|
68
|
+
@predicate = rdf_node.null? ? nil : Node.new(rdf_node)
|
69
|
+
end
|
46
70
|
end
|
47
71
|
|
48
72
|
# Object of the statement.
|
49
73
|
#
|
50
74
|
# @return [Node, nil]
|
51
75
|
def object
|
52
|
-
|
53
|
-
|
76
|
+
if instance_variable_defined?(:@object)
|
77
|
+
@object
|
78
|
+
else
|
79
|
+
rdf_node = Redland.librdf_statement_get_object(rdf_statement)
|
80
|
+
@object = rdf_node.null? ? nil : Node.new(rdf_node)
|
81
|
+
end
|
54
82
|
end
|
55
83
|
|
56
84
|
# Set the subject of the statement
|
@@ -58,7 +86,8 @@ module Redlander
|
|
58
86
|
# @param [Node, nil] node
|
59
87
|
# @return [void]
|
60
88
|
def subject=(node)
|
61
|
-
Redland.librdf_statement_set_subject(
|
89
|
+
Redland.librdf_statement_set_subject(rdf_statement, rdf_node_from(node))
|
90
|
+
@subject = node
|
62
91
|
end
|
63
92
|
|
64
93
|
# Set the predicate of the statement
|
@@ -66,7 +95,8 @@ module Redlander
|
|
66
95
|
# @param [Node, nil] node
|
67
96
|
# @return [void]
|
68
97
|
def predicate=(node)
|
69
|
-
Redland.librdf_statement_set_predicate(
|
98
|
+
Redland.librdf_statement_set_predicate(rdf_statement, rdf_node_from(node))
|
99
|
+
@predicate = node
|
70
100
|
end
|
71
101
|
|
72
102
|
# Set the object of the statement
|
@@ -74,7 +104,8 @@ module Redlander
|
|
74
104
|
# @param [Node, nil] node
|
75
105
|
# @return [void]
|
76
106
|
def object=(node)
|
77
|
-
Redland.librdf_statement_set_object(
|
107
|
+
Redland.librdf_statement_set_object(rdf_statement, rdf_node_from(node))
|
108
|
+
@object = node
|
78
109
|
end
|
79
110
|
|
80
111
|
def eql?(other_statement)
|
@@ -89,7 +120,7 @@ module Redlander
|
|
89
120
|
end
|
90
121
|
|
91
122
|
def to_s
|
92
|
-
Redland.librdf_statement_to_string(
|
123
|
+
Redland.librdf_statement_to_string(rdf_statement)
|
93
124
|
end
|
94
125
|
|
95
126
|
|
@@ -112,9 +143,9 @@ module Redlander
|
|
112
143
|
when NilClass
|
113
144
|
nil
|
114
145
|
when Node
|
115
|
-
source.rdf_node
|
146
|
+
Redland.librdf_new_node_from_node(source.rdf_node)
|
116
147
|
else
|
117
|
-
Node.new(source).rdf_node
|
148
|
+
Redland.librdf_new_node_from_node(Node.new(source).rdf_node)
|
118
149
|
end
|
119
150
|
end
|
120
151
|
end
|
data/lib/redlander/uri.rb
CHANGED
@@ -3,7 +3,30 @@ module Redlander
|
|
3
3
|
# Uri (for internal use)
|
4
4
|
class Uri
|
5
5
|
# @api private
|
6
|
-
|
6
|
+
def rdf_uri
|
7
|
+
unless instance_variable_defined?(:@rdf_uri)
|
8
|
+
@rdf_uri = case @source
|
9
|
+
when FFI::Pointer
|
10
|
+
@source
|
11
|
+
when URI, String
|
12
|
+
Redland.librdf_new_uri(Redlander.rdf_world, @source.to_s)
|
13
|
+
else
|
14
|
+
raise NotImplementedError, "Cannot create Uri from '#{@source.inspect}'"
|
15
|
+
end
|
16
|
+
raise RedlandError, "Failed to create Uri from '#{@source.inspect}'" if @rdf_uri.null?
|
17
|
+
ObjectSpace.define_finalizer(self, self.class.send(:finalize_uri, @rdf_uri))
|
18
|
+
end
|
19
|
+
@rdf_uri
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
private
|
24
|
+
|
25
|
+
# @api private
|
26
|
+
def finalize_uri(rdf_uri_ptr)
|
27
|
+
proc { Redland.librdf_free_uri(rdf_uri_ptr) }
|
28
|
+
end
|
29
|
+
end
|
7
30
|
|
8
31
|
# Create Redlander::Uri
|
9
32
|
#
|
@@ -11,24 +34,15 @@ module Redlander
|
|
11
34
|
# @raise [NotImplementedError] if cannot create a Uri from the given source.
|
12
35
|
# @raise [RedlandError] if it fails to create a Uri.
|
13
36
|
def initialize(source)
|
14
|
-
@
|
15
|
-
when FFI::Pointer
|
16
|
-
wrap(source)
|
17
|
-
when URI, String
|
18
|
-
Redland.librdf_new_uri(Redlander.rdf_world, source.to_s)
|
19
|
-
else
|
20
|
-
raise NotImplementedError, "Cannot create Uri from '#{source.inspect}'"
|
21
|
-
end
|
22
|
-
raise RedlandError, "Failed to create Uri from '#{source.inspect}'" if @rdf_uri.null?
|
23
|
-
ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_uri(@rdf_uri) })
|
37
|
+
@source = source.is_a?(FFI::Pointer) ? wrap(source) : source
|
24
38
|
end
|
25
39
|
|
26
40
|
def to_s
|
27
|
-
Redland.librdf_uri_to_string(
|
41
|
+
Redland.librdf_uri_to_string(rdf_uri)
|
28
42
|
end
|
29
43
|
|
30
44
|
def eql?(other_uri)
|
31
|
-
other_uri.is_a?(Uri) && (Redland.librdf_uri_equals(
|
45
|
+
other_uri.is_a?(Uri) && (Redland.librdf_uri_equals(rdf_uri, other_uri.rdf_uri) != 0)
|
32
46
|
end
|
33
47
|
alias_method :==, :eql?
|
34
48
|
|
data/lib/redlander/version.rb
CHANGED
@@ -0,0 +1,82 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "garbage collection" do
|
4
|
+
|
5
|
+
describe Uri do
|
6
|
+
it "is garbage collected when destroyed" do
|
7
|
+
Uri.new("http://example.com/subject")
|
8
|
+
expect(ObjectSpace.each_object(Uri).count).to eq(1)
|
9
|
+
GC.start
|
10
|
+
expect(ObjectSpace.each_object(Uri).count).to eq(0)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe Node do
|
15
|
+
it "is garbage collected when destroyed" do
|
16
|
+
Node.new(test_statement[:subject])
|
17
|
+
expect(ObjectSpace.each_object(Node).count).to eq(1)
|
18
|
+
GC.start
|
19
|
+
expect(ObjectSpace.each_object(Node).count).to eq(0)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Statement do
|
24
|
+
it "is garbage collected when destroyed" do
|
25
|
+
Statement.new(test_statement)
|
26
|
+
expect(ObjectSpace.each_object(Statement).count).to eq(1)
|
27
|
+
GC.start
|
28
|
+
expect(ObjectSpace.each_object(Statement).count).to eq(0)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe Model do
|
33
|
+
it "is garbage collected when destroyed" do
|
34
|
+
Model.new
|
35
|
+
expect(ObjectSpace.each_object(Model).count).to eq(1)
|
36
|
+
GC.start
|
37
|
+
expect(ObjectSpace.each_object(Model).count).to eq(0)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe Query::Results do
|
42
|
+
it "is garbage collected when destroyed" do
|
43
|
+
model = Model.new
|
44
|
+
model.statements.add(Statement.new(test_statement))
|
45
|
+
model.query("select * {?s ?p ?o}")
|
46
|
+
expect(ObjectSpace.each_object(Query::Results).count).to eq(1)
|
47
|
+
GC.start
|
48
|
+
expect(ObjectSpace.each_object(Query::Results).count).to eq(0)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "collects all objects after using Model" do
|
53
|
+
model = Model.new
|
54
|
+
expect(ObjectSpace.each_object(Model).count).to eq(1)
|
55
|
+
|
56
|
+
model.statements.add(Statement.new(test_statement))
|
57
|
+
expect(ObjectSpace.each_object(Node).count).to eq(3)
|
58
|
+
expect(ObjectSpace.each_object(Statement).count).to eq(1)
|
59
|
+
|
60
|
+
model.query("select * {?s ?p ?o}")
|
61
|
+
expect(ObjectSpace.each_object(Node).count).to eq(6)
|
62
|
+
expect(ObjectSpace.each_object(Query::Results).count).to eq(1)
|
63
|
+
|
64
|
+
# allow model to be garbage collected
|
65
|
+
model = nil
|
66
|
+
|
67
|
+
GC.start
|
68
|
+
expect(ObjectSpace.each_object(Model).count).to eq(0)
|
69
|
+
expect(ObjectSpace.each_object(Statement).count).to eq(0)
|
70
|
+
expect(ObjectSpace.each_object(Query::Results).count).to eq(0)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def test_statement
|
76
|
+
{
|
77
|
+
:subject => URI.parse("http://example.com/subject"),
|
78
|
+
:predicate => URI.parse("http://example.com/predicate"),
|
79
|
+
:object => URI.parse("object!")
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redlander
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Slava Kravchenko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: xml_schema
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- spec/fixtures/doap.nt
|
85
85
|
- spec/fixtures/doap.rdf
|
86
86
|
- spec/fixtures/doap.ttl
|
87
|
+
- spec/integration/finalizer_gc_spec.rb
|
87
88
|
- spec/integration/memory_leak_spec.rb
|
88
89
|
- spec/lib/redlander/model_spec.rb
|
89
90
|
- spec/lib/redlander/node_spec.rb
|
@@ -118,6 +119,7 @@ test_files:
|
|
118
119
|
- spec/fixtures/doap.nt
|
119
120
|
- spec/fixtures/doap.rdf
|
120
121
|
- spec/fixtures/doap.ttl
|
122
|
+
- spec/integration/finalizer_gc_spec.rb
|
121
123
|
- spec/integration/memory_leak_spec.rb
|
122
124
|
- spec/lib/redlander/model_spec.rb
|
123
125
|
- spec/lib/redlander/node_spec.rb
|