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