activerdf 1.0 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +10 -5
- data/Rakefile +13 -1
- data/lib/active_rdf/federation/active_rdf_adapter.rb +2 -6
- data/lib/active_rdf/federation/connection_pool.rb +22 -13
- data/lib/active_rdf/federation/federation_manager.rb +4 -12
- data/lib/active_rdf/objectmanager/namespace.rb +3 -9
- data/lib/active_rdf/objectmanager/object_manager.rb +12 -16
- data/lib/active_rdf/objectmanager/resource.rb +53 -56
- data/lib/active_rdf/queryengine/query.rb +69 -66
- data/lib/active_rdf/queryengine/query2jars2.rb +3 -8
- data/lib/active_rdf/queryengine/query2sparql.rb +3 -7
- data/lib/active_rdf.rb +7 -9
- data/lib/active_rdf_helpers.rb +2 -2
- data/lib/active_rdf_log.rb +5 -16
- data/test/objectmanager/test_resource_reading.rb +13 -13
- data/test/queryengine/test_query.rb +8 -10
- metadata +2 -2
data/README
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
== Welcome to ActiveRDF
|
2
|
-
|
3
1
|
ActiveRDF is a library for accessing RDF data from Ruby programs. It can be used
|
4
2
|
as data layer in Ruby-on-Rails, similar to ActiveRecord (which provides an O/R
|
5
3
|
mapping to relational databases). ActiveRDF in RoR allows you to create semantic
|
@@ -10,16 +8,23 @@ etc. programmatically, without queries.
|
|
10
8
|
See http://www.activerdf.org for more information.
|
11
9
|
|
12
10
|
== Getting Started
|
11
|
+
|
12
|
+
See http://wiki.activerdf.org/GettingStartedGuide for information on how to install
|
13
|
+
ActiveRDF, get it running with some data sources, and start using it.
|
14
|
+
|
15
|
+
== Simple Example
|
16
|
+
|
17
|
+
The following example uses a SPARQL endpoint with FOAF data and displays all
|
18
|
+
people found in the data source:
|
19
|
+
|
13
20
|
require 'active_rdf'
|
14
21
|
ConnectionPool.add_data_source :type => :sparql, :host => '...'
|
15
22
|
Namespace.register :foaf, 'http://xmlns.com/foaf/0.1/'
|
16
23
|
ObjectManager.construct_classes
|
17
24
|
people = FOAF::Person.find_all
|
18
25
|
|
19
|
-
See http://activerdf.org for more examples.
|
20
|
-
|
21
26
|
== License
|
22
|
-
ActiveRDF is distributed under the LGPL[link
|
27
|
+
ActiveRDF is distributed under the LGPL[link:http://www.gnu.org/licenses/lgpl.html] license.
|
23
28
|
|
24
29
|
== Authors
|
25
30
|
* Eyal Oren
|
data/Rakefile
CHANGED
@@ -11,7 +11,19 @@ include FileUtils
|
|
11
11
|
# setup tests and rdoc files
|
12
12
|
setup_tests
|
13
13
|
setup_clean ["pkg", "lib/*.bundle", "*.gem", ".config"]
|
14
|
-
|
14
|
+
|
15
|
+
# setup rdoc task
|
16
|
+
#setup_rdoc ['README', 'LICENSE', 'lib/**/*.rb']
|
17
|
+
Rake::RDocTask.new do |rdoc|
|
18
|
+
files = ['README', 'LICENSE', 'lib/**/*.rb', 'doc/**/*.rdoc', 'test/*.rb']
|
19
|
+
files << 'activerdf-*/lib/**/*.rb'
|
20
|
+
rdoc.rdoc_files.add(files)
|
21
|
+
rdoc.main = "README" # page to start on
|
22
|
+
rdoc.title = "ActiveRDF documentation"
|
23
|
+
rdoc.template = "tools/allison/allison.rb"
|
24
|
+
rdoc.rdoc_dir = 'doc' # rdoc output folder
|
25
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
26
|
+
end
|
15
27
|
|
16
28
|
# default task: install
|
17
29
|
desc 'test and package gem'
|
@@ -1,11 +1,8 @@
|
|
1
|
-
# Author:: Benjamin Heitmann
|
2
|
-
# Copyright:: (c) 2005-2006
|
3
|
-
# License:: LGPL
|
4
|
-
|
5
1
|
require 'active_rdf'
|
6
2
|
require 'queryengine/query2sparql'
|
7
3
|
|
8
|
-
#
|
4
|
+
# Generic superclass of all adapters
|
5
|
+
|
9
6
|
class ActiveRdfAdapter
|
10
7
|
# indicate if adapter can read and write
|
11
8
|
bool_accessor :reads, :writes
|
@@ -14,5 +11,4 @@ class ActiveRdfAdapter
|
|
14
11
|
def translate(query)
|
15
12
|
Query2SPARQL.translate(query)
|
16
13
|
end
|
17
|
-
|
18
14
|
end
|
@@ -1,13 +1,9 @@
|
|
1
|
-
# Maintains pool of adapter instances that are connected to datasources
|
2
|
-
# returns right adapter for a given datasource, by either reusing
|
3
|
-
# existing adapter-instance or creating new adapter-instance
|
4
|
-
#
|
5
|
-
# Author:: Eyal Oren
|
6
|
-
# Copyright:: (c) 2005-2006
|
7
|
-
# License:: LGPL
|
8
|
-
|
9
1
|
require 'active_rdf'
|
10
2
|
|
3
|
+
# Maintains pool of adapter instances that are connected to datasources. Returns
|
4
|
+
# right adapter for a given datasource, by either reusing an
|
5
|
+
# existing adapter-instance or creating new a adapter-instance.
|
6
|
+
|
11
7
|
class ConnectionPool
|
12
8
|
class << self
|
13
9
|
attr_accessor :write_adapter
|
@@ -37,7 +33,7 @@ class ConnectionPool
|
|
37
33
|
|
38
34
|
# clears the pool: removes all registered data sources
|
39
35
|
def ConnectionPool.clear
|
40
|
-
$
|
36
|
+
$activerdflog.info "ConnectionPool: clear called"
|
41
37
|
@@adapter_pool = []
|
42
38
|
@@adapter_parameters = []
|
43
39
|
self.write_adapter = nil
|
@@ -59,7 +55,7 @@ class ConnectionPool
|
|
59
55
|
|
60
56
|
# returns adapter-instance for given parameters (either existing or new)
|
61
57
|
def ConnectionPool.add_data_source(connection_params)
|
62
|
-
$
|
58
|
+
$activerdflog.info "ConnectionPool: add_data_source with params: #{connection_params.inspect}"
|
63
59
|
|
64
60
|
# either get the adapter-instance from the pool
|
65
61
|
# or create new one (and add it to the pool)
|
@@ -68,14 +64,14 @@ class ConnectionPool
|
|
68
64
|
# adapter not in the pool yet: create it,
|
69
65
|
# register its connection parameters in parameters-array
|
70
66
|
# and add it to the pool (at same index-position as parameters)
|
71
|
-
$
|
67
|
+
$activerdflog.debug("Create a new adapter for parameters #{connection_params.inspect}")
|
72
68
|
adapter = create_adapter(connection_params)
|
73
69
|
@@adapter_parameters << connection_params
|
74
70
|
@@adapter_pool << adapter
|
75
71
|
else
|
76
72
|
# if adapter parametrs registered already,
|
77
73
|
# then adapter must be in the pool, at the same index-position as its parameters
|
78
|
-
$
|
74
|
+
$activerdflog.debug("Reusing existing adapter")
|
79
75
|
adapter = @@adapter_pool[index]
|
80
76
|
end
|
81
77
|
|
@@ -85,6 +81,19 @@ class ConnectionPool
|
|
85
81
|
return adapter
|
86
82
|
end
|
87
83
|
|
84
|
+
# sets adapter-instance for connection parameters (if you want to re-enable an existing adapter)
|
85
|
+
def ConnectionPool.set_data_source(adapter, connection_params = {})
|
86
|
+
index = @@adapter_parameters.index(connection_params)
|
87
|
+
if index.nil?
|
88
|
+
@@adapter_parameters << connection_params
|
89
|
+
@@adapter_pool << adapter
|
90
|
+
else
|
91
|
+
@@adapter_pool[index] = adapter
|
92
|
+
end
|
93
|
+
self.write_adapter = adapter if adapter.writes?
|
94
|
+
adapter
|
95
|
+
end
|
96
|
+
|
88
97
|
# aliasing add_data_source as add
|
89
98
|
# (code bit more complicad since they are class methods)
|
90
99
|
class << self
|
@@ -94,7 +103,7 @@ class ConnectionPool
|
|
94
103
|
# adapter-types can register themselves with connection pool by
|
95
104
|
# indicating which adapter-type they are
|
96
105
|
def ConnectionPool.register_adapter(type, klass)
|
97
|
-
$
|
106
|
+
$activerdflog.info "ConnectionPool: registering adapter of type #{type} for class #{klass}"
|
98
107
|
@@registered_adapter_types[type] = klass
|
99
108
|
end
|
100
109
|
|
@@ -1,16 +1,12 @@
|
|
1
|
-
# Manages the federation of datasources
|
2
|
-
# distributes queries to right datasources and merges their results
|
3
|
-
#
|
4
|
-
# Author:: Eyal Oren
|
5
|
-
# Copyright:: (c) 2005-2006
|
6
|
-
# License:: LGPL
|
7
1
|
require 'federation/connection_pool'
|
8
2
|
|
3
|
+
# Manages the federation of datasources: distributes queries to right
|
4
|
+
# datasources and merges their results
|
5
|
+
|
9
6
|
class FederationManager
|
10
7
|
# add triple s,p,o to the currently selected write-adapter
|
11
8
|
def FederationManager.add(s,p,o)
|
12
9
|
# TODO: allow addition of full graphs
|
13
|
-
$log.debug "FederationManager: add: triple is #{s} #{p} #{o}"
|
14
10
|
ConnectionPool.write_adapter.add(s,p,o)
|
15
11
|
end
|
16
12
|
|
@@ -22,7 +18,6 @@ class FederationManager
|
|
22
18
|
raise ActiveRdfError, "cannot execute query without data sources"
|
23
19
|
end
|
24
20
|
|
25
|
-
$log.debug "FederationManager: query called with: #{q}"
|
26
21
|
# ask each adapter for query results
|
27
22
|
# and yield them consequtively
|
28
23
|
if block_given?
|
@@ -72,9 +67,6 @@ class FederationManager
|
|
72
67
|
end
|
73
68
|
end
|
74
69
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
return final_results
|
70
|
+
final_results
|
79
71
|
end
|
80
72
|
end
|
@@ -1,13 +1,8 @@
|
|
1
|
-
# Manages namespace abbreviations and expansions
|
2
|
-
#
|
3
|
-
# Author:: Eyal Oren
|
4
|
-
# Copyright:: (c) 2005-2006
|
5
|
-
# License:: LGPL
|
6
|
-
|
7
1
|
require 'active_rdf'
|
8
2
|
|
9
|
-
|
3
|
+
# Manages namespace abbreviations and expansions
|
10
4
|
|
5
|
+
class Namespace
|
11
6
|
@@namespaces = Hash.new
|
12
7
|
@@inverted_namespaces = Hash.new
|
13
8
|
|
@@ -15,7 +10,7 @@ class Namespace
|
|
15
10
|
# e.g. :rdf and 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
|
16
11
|
def self.register(prefix, fullURI)
|
17
12
|
raise ActiveRdfError, 'prefix nor uri can be empty' if (prefix.to_s.empty? or fullURI.to_s.empty?)
|
18
|
-
$
|
13
|
+
$activerdflog.info "Namespace: registering #{fullURI} to #{prefix}"
|
19
14
|
@@namespaces[prefix.to_sym] = fullURI.to_s
|
20
15
|
@@inverted_namespaces[fullURI.to_s] = prefix.to_sym
|
21
16
|
end
|
@@ -23,7 +18,6 @@ class Namespace
|
|
23
18
|
# returns a resource whose URI is formed by concatenation of prefix and localname
|
24
19
|
def self.lookup(prefix, localname)
|
25
20
|
full_resource = expand(prefix, localname)
|
26
|
-
$log.debug "Namespace: lookup for Resource #{full_resource} initiated"
|
27
21
|
RDFS::Resource.new(expand(prefix, localname))
|
28
22
|
end
|
29
23
|
|
@@ -1,15 +1,11 @@
|
|
1
|
-
# Constructs Ruby classes for RDFS classes (in the right namespace)
|
2
|
-
#
|
3
|
-
# Author:: Eyal Oren
|
4
|
-
# Copyright:: (c) 2005-2006
|
5
|
-
# License:: LGPL
|
6
1
|
require 'active_rdf'
|
7
2
|
|
3
|
+
# Constructs Ruby classes for RDFS classes (in the right namespace)
|
4
|
+
|
8
5
|
class ObjectManager
|
9
|
-
|
10
|
-
#
|
11
|
-
|
12
|
-
# getting symbol undefined errors (because e.g. foaf:person wasnt encountered
|
6
|
+
# Constructs empty Ruby classes for all RDF types found in the data. Allows
|
7
|
+
# users to invoke methods on classes (e.g. FOAF::Person) without
|
8
|
+
# getting symbol undefined errors (because e.g. foaf:person wasnt encountered
|
13
9
|
# before so no class was created for it)
|
14
10
|
def self.construct_classes
|
15
11
|
# find all rdf:types and construct class for each of them
|
@@ -31,7 +27,7 @@ class ObjectManager
|
|
31
27
|
# flattening to get rid of nested arrays
|
32
28
|
# compacting array to get rid of nil (if one of these queries returned nil)
|
33
29
|
klasses = klasses.flatten.compact
|
34
|
-
$
|
30
|
+
$activerdflog.debug "ObjectManager: construct_classes: classes found: #{klasses}"
|
35
31
|
|
36
32
|
# then we construct a Ruby class for each found rdfs:class
|
37
33
|
# and return the set of all constructed classes
|
@@ -51,31 +47,31 @@ class ObjectManager
|
|
51
47
|
if prefix.nil?
|
52
48
|
# if the prefix is unknown, we create our own from the full URI
|
53
49
|
modulename = create_module_name(resource)
|
54
|
-
$
|
50
|
+
$activerdflog.debug "ObjectManager: construct_class: constructing modulename #{modulename} from URI #{resource}"
|
55
51
|
else
|
56
52
|
# otherwise we convert the registered prefix into a module name
|
57
53
|
modulename = prefix_to_module(prefix)
|
58
|
-
$
|
54
|
+
$activerdflog.debug "ObjectManager: construct_class: constructing modulename #{modulename} from registered prefix #{prefix}"
|
59
55
|
end
|
60
56
|
klassname = localname_to_class(localname)
|
61
57
|
|
62
58
|
# look whether module defined
|
63
59
|
# else: create it
|
64
60
|
_module = if Object.const_defined?(modulename.to_sym)
|
65
|
-
$
|
61
|
+
$activerdflog.debug "ObjectManager: construct_class: module name #{modulename} previously defined"
|
66
62
|
Object.const_get(modulename.to_sym)
|
67
63
|
else
|
68
|
-
$
|
64
|
+
$activerdflog.debug "ObjectManager: construct_class: defining module name #{modulename} now"
|
69
65
|
Object.const_set(modulename, Module.new)
|
70
66
|
end
|
71
67
|
|
72
68
|
# look whether class defined in that module
|
73
69
|
if _module.const_defined?(klassname.to_sym)
|
74
|
-
$
|
70
|
+
$activerdflog.debug "ObjectManager: construct_class: given class #{klassname} defined in the module"
|
75
71
|
# if so, return the existing class
|
76
72
|
_module.const_get(klassname.to_sym)
|
77
73
|
else
|
78
|
-
$
|
74
|
+
$activerdflog.debug "ObjectManager: construct_class: creating given class #{klassname}"
|
79
75
|
# otherwise: create it, inside that module, as subclass of RDFS::Resource
|
80
76
|
# (using toplevel Class.new to prevent RDFS::Class.new from being called)
|
81
77
|
klass = _module.module_eval("#{klassname} = Object::Class.new(RDFS::Resource)")
|
@@ -1,19 +1,14 @@
|
|
1
|
-
# Represents an RDF resource and manages manipulations of that resource,
|
2
|
-
# including data lookup (e.g. eyal.age), data updates (e.g. eyal.age=20),
|
3
|
-
# class-level lookup (Person.find_by_name 'eyal'), and class-membership
|
4
|
-
# (eyal.class ...Person)
|
5
|
-
#
|
6
|
-
# Author:: Eyal Oren
|
7
|
-
# Copyright:: (c) 2005-2006
|
8
|
-
# License:: LGPL
|
9
|
-
|
10
1
|
require 'active_rdf'
|
11
2
|
require 'objectmanager/object_manager'
|
12
3
|
require 'objectmanager/namespace'
|
13
4
|
require 'queryengine/query'
|
14
5
|
|
15
|
-
# TODO: add unit test to validate class construction and queries on them
|
16
6
|
module RDFS
|
7
|
+
# Represents an RDF resource and manages manipulations of that resource,
|
8
|
+
# including data lookup (e.g. eyal.age), data updates (e.g. eyal.age=20),
|
9
|
+
# class-level lookup (Person.find_by_name 'eyal'), and class-membership
|
10
|
+
# (eyal.class ...Person).
|
11
|
+
|
17
12
|
class RDFS::Resource
|
18
13
|
# adding accessor to the class uri:
|
19
14
|
# the uri of the rdf resource being represented by this class
|
@@ -27,8 +22,6 @@ module RDFS
|
|
27
22
|
# creates new resource representing an RDF resource
|
28
23
|
def initialize uri
|
29
24
|
raise ActiveRdfError, "creating resource <#{uri}>" unless uri.is_a?(String)
|
30
|
-
|
31
|
-
# $log.debug "RDFS::Resource new: initializing new Resource with #{uri}"
|
32
25
|
@uri = uri
|
33
26
|
end
|
34
27
|
|
@@ -77,7 +70,7 @@ module RDFS
|
|
77
70
|
def Resource.method_missing(method, *args)
|
78
71
|
method_name = method.to_s
|
79
72
|
|
80
|
-
$
|
73
|
+
$activerdflog.debug "RDFS::Resource: method_missing on class: called with method name #{method}"
|
81
74
|
|
82
75
|
# extract predicates on which to match
|
83
76
|
# e.g. find_by_name, find_by_name_and_age
|
@@ -102,12 +95,12 @@ module RDFS
|
|
102
95
|
end
|
103
96
|
|
104
97
|
# execute query
|
105
|
-
$
|
106
|
-
return query.execute
|
98
|
+
$activerdflog.debug "RDFS::Resource: method_missing on class: executing query: #{query}"
|
99
|
+
return query.execute(:flatten => true)
|
107
100
|
end
|
108
101
|
|
109
102
|
# otherwise, if no match found, raise NoMethodError (in superclass)
|
110
|
-
$
|
103
|
+
$activerdflog.warn 'RDFS::Resource: method_missing on class: method not matching find_by_*'
|
111
104
|
super
|
112
105
|
end
|
113
106
|
|
@@ -156,7 +149,7 @@ module RDFS
|
|
156
149
|
# cheaper than (1)-(2) but (1) and (2) are probably more probable (getting
|
157
150
|
# attribute values over executing custom methods)
|
158
151
|
|
159
|
-
$
|
152
|
+
$activerdflog.debug "RDFS::Resource: method_missing on instance: called with method name #{method}"
|
160
153
|
|
161
154
|
# are we doing an update or not?
|
162
155
|
# checking if method ends with '='
|
@@ -170,7 +163,7 @@ module RDFS
|
|
170
163
|
end
|
171
164
|
|
172
165
|
candidates = if update
|
173
|
-
class_level_predicates
|
166
|
+
(class_level_predicates + direct_predicates).compact.uniq
|
174
167
|
else
|
175
168
|
direct_predicates
|
176
169
|
end
|
@@ -198,7 +191,8 @@ module RDFS
|
|
198
191
|
end
|
199
192
|
end
|
200
193
|
|
201
|
-
|
194
|
+
raise ActiveRdfError, "could not set #{methodname} to #{args}: no suitable
|
195
|
+
predicate found. Maybe you are missing some schema information?" if update
|
202
196
|
|
203
197
|
# get/set attribute value did not succeed, so checking option (2) and (4)
|
204
198
|
|
@@ -225,8 +219,8 @@ module RDFS
|
|
225
219
|
# checking possibility (4)
|
226
220
|
# TODO: implement search strategy to select in which class to invoke
|
227
221
|
# e.g. if to_s defined in Resource and in Person we should use Person
|
228
|
-
$
|
229
|
-
self.
|
222
|
+
$activerdflog.debug "RDFS::Resource: method_missing option 4: custom class method"
|
223
|
+
self.type.each do |klass|
|
230
224
|
if klass.instance_methods.include?(method.to_s)
|
231
225
|
_dup = klass.new(uri)
|
232
226
|
return _dup.send(method,*args)
|
@@ -241,20 +235,28 @@ module RDFS
|
|
241
235
|
nil
|
242
236
|
end
|
243
237
|
|
244
|
-
|
245
|
-
|
238
|
+
# saves instance into datastore
|
239
|
+
def save
|
240
|
+
db = ConnectionPool.write_adapter
|
241
|
+
rdftype = Namespace.lookup(:rdf, :type)
|
242
|
+
types.each do |t|
|
243
|
+
db.add(self, rdftype, t)
|
244
|
+
end
|
245
|
+
|
246
|
+
Query.new.distinct(:p,:o).where(self, :p, :o).execute do |p, o|
|
247
|
+
db.add(self, p, o)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def type
|
246
252
|
types.collect do |type|
|
247
253
|
ObjectManager.construct_class(type)
|
248
254
|
end
|
249
255
|
end
|
250
256
|
|
251
|
-
def type
|
252
|
-
get_property_value(Namespace.lookup(:rdf,:type))
|
253
|
-
end
|
254
|
-
|
255
257
|
# overrides built-in instance_of? to use rdf:type definitions
|
256
258
|
def instance_of?(klass)
|
257
|
-
self.
|
259
|
+
self.type.include?(klass)
|
258
260
|
end
|
259
261
|
|
260
262
|
# returns all predicates that fall into the domain of the rdf:type of this
|
@@ -272,54 +274,49 @@ module RDFS
|
|
272
274
|
else
|
273
275
|
q = Query.new.select(:p)
|
274
276
|
end
|
275
|
-
q.where(self,:p, :o).execute
|
277
|
+
q.where(self,:p, :o).execute
|
276
278
|
end
|
277
279
|
|
278
280
|
def property_accessors
|
279
281
|
direct_predicates.collect {|pred| Namespace.localname(pred) }
|
280
282
|
end
|
281
283
|
|
282
|
-
# returns all rdf:types of this resource
|
283
|
-
def types
|
284
|
-
type = Namespace.lookup(:rdf, :type)
|
285
|
-
|
286
|
-
# we lookup the type in the database
|
287
|
-
types = Query.new.distinct(:t).where(self,type,:t).execute(:flatten => false)
|
288
|
-
|
289
|
-
# if we dont know it, we return Resource (as toplevel)
|
290
|
-
# this should in theory actually never happen (since any node is a rdfs:Resource)
|
291
|
-
# but could happen if the subject is unknown to the database
|
292
|
-
# or if the database does not support RDFS inferencing
|
293
|
-
return [Namespace.lookup(:rdfs,"Resource")] if types.empty?
|
294
|
-
return types
|
295
|
-
end
|
296
|
-
|
297
284
|
# alias include? to ==, so that you can do paper.creator.include?(eyal)
|
298
285
|
# without worrying whether paper.creator is single- or multi-valued
|
299
286
|
alias include? ==
|
300
287
|
|
301
288
|
# returns uri of resource, can be overridden in subclasses
|
302
289
|
def to_s
|
303
|
-
"
|
290
|
+
"<#{uri}>"
|
304
291
|
end
|
305
292
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
# empty labels are not useful: replace them by localname
|
310
|
-
label = Namespace.localname(self) if label.empty?
|
311
|
-
|
312
|
-
# if we have no localname, use full uri
|
313
|
-
label = uri if label.empty?
|
314
|
-
|
315
|
-
label
|
293
|
+
# label of resource (rdfs:label if available, uri otherwise)
|
294
|
+
def label
|
295
|
+
get_property_value(Namespace.lookup(:rdfs,:label)) || uri
|
316
296
|
end
|
317
297
|
|
318
298
|
private
|
319
299
|
def get_property_value(predicate, args=[])
|
320
300
|
return_ary = args[0][:array] if args[0].is_a?(Hash)
|
321
301
|
flatten_results = !return_ary
|
322
|
-
Query.new.distinct(:o).where(self, predicate, :o)
|
302
|
+
query = Query.new.distinct(:o).where(self, predicate, :o)
|
303
|
+
query.execute(:flatten => flatten_results)
|
323
304
|
end
|
305
|
+
|
306
|
+
# returns all rdf:types of this resource
|
307
|
+
def types
|
308
|
+
type = Namespace.lookup(:rdf, :type)
|
309
|
+
|
310
|
+
# we lookup the type in the database
|
311
|
+
types = Query.new.distinct(:t).where(self,type,:t).execute
|
312
|
+
|
313
|
+
# we are also always of type rdfs:resource and of our own class (e.g. foaf:Person)
|
314
|
+
defaults = []
|
315
|
+
defaults << Namespace.lookup(:rdfs,:Resource)
|
316
|
+
defaults << self.class.class_uri
|
317
|
+
|
318
|
+
(types + defaults).uniq
|
319
|
+
end
|
320
|
+
|
324
321
|
end
|
325
322
|
end
|
@@ -1,73 +1,88 @@
|
|
1
|
-
# Represents a query on a datasource, abstract representation of SPARQL features
|
2
|
-
# is passed to federation/adapter for execution on data
|
3
|
-
#
|
4
|
-
# Author:: Eyal Oren
|
5
|
-
# Copyright:: (c) 2005-2006
|
6
|
-
# License:: LGPL
|
7
1
|
require 'active_rdf'
|
8
2
|
require 'federation/federation_manager'
|
9
3
|
|
10
|
-
#
|
11
|
-
|
4
|
+
# Represents a query on a datasource, abstract representation of SPARQL
|
5
|
+
# features. Query is passed to federation manager or adapter for execution on
|
6
|
+
# data source. In all clauses symbols represent variables:
|
7
|
+
# Query.new.select(:s).where(:s,:p,:o).
|
12
8
|
class Query
|
13
|
-
|
14
|
-
|
9
|
+
attr_reader :select_clauses, :where_clauses, :sort_clauses, :keywords, :limits, :offsets
|
10
|
+
bool_accessor :distinct, :ask, :select, :count, :keyword, :reasoning
|
15
11
|
|
16
|
-
|
17
|
-
|
18
|
-
distinct = false
|
12
|
+
def initialize
|
13
|
+
distinct = false
|
19
14
|
limit = nil
|
20
15
|
offset = nil
|
21
|
-
|
22
|
-
|
16
|
+
@select_clauses = []
|
17
|
+
@where_clauses = []
|
18
|
+
@sort_clauses = []
|
23
19
|
@keywords = {}
|
24
|
-
|
20
|
+
@reasoning = true
|
21
|
+
end
|
25
22
|
|
23
|
+
# Clears the select clauses
|
26
24
|
def clear_select
|
27
|
-
|
25
|
+
$activerdflog.debug "cleared select clause"
|
28
26
|
@select_clauses = []
|
29
27
|
distinct = false
|
30
28
|
end
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
# Adds variables to select clause
|
31
|
+
def select *s
|
32
|
+
@select = true
|
33
|
+
s.each do |e|
|
34
|
+
@select_clauses << parametrise(e)
|
35
|
+
end
|
37
36
|
# removing duplicate select clauses
|
38
37
|
@select_clauses.uniq!
|
39
|
-
|
40
|
-
|
41
|
-
end
|
38
|
+
self
|
39
|
+
end
|
42
40
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
41
|
+
# Adds variables to ask clause (see SPARQL specification)
|
42
|
+
def ask
|
43
|
+
@ask = true
|
44
|
+
self
|
45
|
+
end
|
48
46
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
47
|
+
# Adds variables to select distinct clause
|
48
|
+
def distinct *s
|
49
|
+
@distinct = true
|
50
|
+
select(*s)
|
51
|
+
end
|
52
|
+
alias_method :select_distinct, :distinct
|
54
53
|
|
54
|
+
# Adds variables to count clause
|
55
55
|
def count *s
|
56
56
|
@count = true
|
57
57
|
select(*s)
|
58
58
|
end
|
59
59
|
|
60
|
+
# Adds sort predicates (must appear in select clause)
|
61
|
+
def sort *s
|
62
|
+
s.each do |e|
|
63
|
+
@sort_clauses << parametrise(e)
|
64
|
+
end
|
65
|
+
# removing duplicate select clauses
|
66
|
+
@sort_clauses.uniq!
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
# Adds limit clause (maximum number of results to return)
|
60
71
|
def limit(i)
|
61
72
|
@limits = i
|
62
73
|
self
|
63
74
|
end
|
64
75
|
|
76
|
+
# Add offset clause (ignore first n results)
|
65
77
|
def offset(i)
|
66
78
|
@offsets = i
|
67
79
|
self
|
68
80
|
end
|
69
81
|
|
70
|
-
|
82
|
+
# Adds where clauses (s,p,o) where each constituent is either variable (:s) or
|
83
|
+
# an RDFS::Resource. Keyword queries are specified with the special :keyword
|
84
|
+
# symbol: Query.new.select(:s).where(:s, :keyword, 'eyal')
|
85
|
+
def where s,p,o,c=nil
|
71
86
|
case p
|
72
87
|
when :keyword
|
73
88
|
# treat keywords in where-clauses specially
|
@@ -79,26 +94,20 @@ class Query
|
|
79
94
|
# generator.
|
80
95
|
# if you construct this query manually, you shouldn't! if your select
|
81
96
|
# variable happens to be in one of the removed clauses: tough luck.
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
$log.debug "Query: where: got a Predicate which is no Symbol, and no RDFS::Resource, but is instead: #{p}"
|
92
|
-
raise(ActiveRdfError, "cannot add a where clause, in which s is not a resource and not a variable")
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
@where_clauses << [s,p,o].collect{|arg| parametrise(arg)}
|
97
|
+
|
98
|
+
unless s.respond_to?(:uri) or s.class == Symbol
|
99
|
+
raise(ActiveRdfError, "cannot add a where clause with s #{s}: s must be a resource or a variable")
|
100
|
+
end
|
101
|
+
unless p.respond_to?(:uri) or p.class == Symbol
|
102
|
+
raise(ActiveRdfError, "cannot add a where clause with p #{p}: p must be a resource or a variable")
|
103
|
+
end
|
104
|
+
|
105
|
+
@where_clauses << [s,p,o,c].collect{|arg| parametrise(arg)}.compact
|
97
106
|
end
|
98
107
|
self
|
99
108
|
end
|
100
109
|
|
101
|
-
#
|
110
|
+
# Adds keyword constraint to the query. You can use all Ferret query syntax in
|
102
111
|
# the constraint (e.g. keyword_where(:s,'eyal|benjamin')
|
103
112
|
def keyword_where s,o
|
104
113
|
@keyword = true
|
@@ -111,24 +120,14 @@ class Query
|
|
111
120
|
self
|
112
121
|
end
|
113
122
|
|
114
|
-
#
|
115
|
-
# def replace_where_clause old,new
|
116
|
-
# return unless where_clauses.includes?(old)
|
117
|
-
# where_clauses.delete(old)
|
118
|
-
# where_clauses.insert(new)
|
119
|
-
# end
|
120
|
-
|
121
|
-
# execute query on data sources
|
122
|
-
# either returns result as array
|
123
|
+
# Executes query on data sources. Either returns result as array
|
123
124
|
# (flattened into single value unless specified otherwise)
|
124
125
|
# or executes a block (number of block variables should be
|
125
126
|
# same as number of select variables)
|
126
127
|
#
|
127
|
-
# usage
|
128
|
-
# usage
|
129
|
-
def execute(options={:flatten =>
|
130
|
-
$log.debug "Query: executing query: #{self.inspect}"
|
131
|
-
|
128
|
+
# usage:: results = query.execute
|
129
|
+
# usage:: query.execute do |s,p,o| ... end
|
130
|
+
def execute(options={:flatten => false}, &block)
|
132
131
|
if block_given?
|
133
132
|
FederationManager.query(self) do |*clauses|
|
134
133
|
block.call(*clauses)
|
@@ -138,6 +137,7 @@ class Query
|
|
138
137
|
end
|
139
138
|
end
|
140
139
|
|
140
|
+
# Returns query string depending on adapter (e.g. SPARQL, N3QL, etc.)
|
141
141
|
def to_s
|
142
142
|
if ConnectionPool.read_adapters.empty?
|
143
143
|
inspect
|
@@ -146,6 +146,7 @@ class Query
|
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
149
|
+
# Returns SPARQL serialisation of query
|
149
150
|
def to_sp
|
150
151
|
require 'queryengine/query2sparql'
|
151
152
|
Query2SPARQL.translate(self)
|
@@ -159,6 +160,8 @@ class Query
|
|
159
160
|
#'?' + s.to_s
|
160
161
|
when RDFS::Resource
|
161
162
|
s
|
163
|
+
when nil
|
164
|
+
nil
|
162
165
|
else
|
163
166
|
'"' + s.to_s + '"'
|
164
167
|
end
|
@@ -1,12 +1,7 @@
|
|
1
|
-
# Translates abstract query into jars2 query
|
2
|
-
# ignores ASK queries
|
3
|
-
#
|
4
|
-
# Author:: Eyal Oren
|
5
|
-
# Copyright:: (c) 2005-2006
|
6
|
-
# License:: LGPL
|
7
1
|
require 'active_rdf'
|
8
2
|
|
9
|
-
|
3
|
+
# Translates abstract query into jars2 query.
|
4
|
+
# (ignores ASK queries)
|
10
5
|
class Query2Jars2
|
11
6
|
def self.translate(query)
|
12
7
|
str = ""
|
@@ -21,7 +16,7 @@ class Query2Jars2
|
|
21
16
|
# figure it out.
|
22
17
|
end
|
23
18
|
|
24
|
-
$
|
19
|
+
$activerdflog.debug "Query2Jars2: translated #{query} to #{str}"
|
25
20
|
return str
|
26
21
|
end
|
27
22
|
end
|
@@ -1,11 +1,7 @@
|
|
1
|
-
# translates abstract query into SPARQL that can be executed on SPARQL-compliant data source
|
2
|
-
#
|
3
|
-
# Author:: Eyal Oren
|
4
|
-
# Copyright:: (c) 2005-2006
|
5
|
-
# License:: LGPL
|
6
1
|
require 'active_rdf'
|
7
2
|
|
8
|
-
|
3
|
+
# Translates abstract query into SPARQL that can be executed on SPARQL-compliant
|
4
|
+
# data source.
|
9
5
|
class Query2SPARQL
|
10
6
|
def self.translate(query)
|
11
7
|
str = ""
|
@@ -19,7 +15,7 @@ class Query2SPARQL
|
|
19
15
|
str << "ASK { #{where_clauses(query)} }"
|
20
16
|
end
|
21
17
|
|
22
|
-
$
|
18
|
+
$activerdflog.debug "Query2SPARQL: translated the query to #{str}"
|
23
19
|
return str
|
24
20
|
end
|
25
21
|
|
data/lib/active_rdf.rb
CHANGED
@@ -1,8 +1,4 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
# Author:: Eyal Oren and Renaud Delbru
|
4
|
-
# Copyright:: (c) 2005-2006 Eyal Oren and Renaud Delbru
|
5
|
-
# License:: LGPL
|
1
|
+
# ActiveRDF loader
|
6
2
|
|
7
3
|
# adding active_rdf subdirectory to the ruby loadpath
|
8
4
|
file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
@@ -13,7 +9,7 @@ $: << this_dir + '/active_rdf/'
|
|
13
9
|
require 'active_rdf_helpers'
|
14
10
|
require 'active_rdf_log'
|
15
11
|
|
16
|
-
$
|
12
|
+
$activerdflog.info "ActiveRDF started, logging level: #{$activerdflog.level}"
|
17
13
|
|
18
14
|
# load standard classes that need to be loaded at startup
|
19
15
|
require 'objectmanager/resource'
|
@@ -26,7 +22,7 @@ def load_adapter s
|
|
26
22
|
begin
|
27
23
|
require s
|
28
24
|
rescue StandardError => e
|
29
|
-
$
|
25
|
+
$activerdflog.info "could not load adapter #{s}: #{e}"
|
30
26
|
end
|
31
27
|
end
|
32
28
|
|
@@ -34,15 +30,17 @@ require 'rubygems'
|
|
34
30
|
#determine if we are installed as a gem right now:
|
35
31
|
if Gem::cache().search("activerdf").empty?
|
36
32
|
#we are not running as a gem
|
37
|
-
$
|
33
|
+
$activerdflog.info 'ActiveRDF is NOT installed as a Gem'
|
38
34
|
load_adapter this_dir + '/../activerdf-rdflite/lib/activerdf_rdflite/rdflite'
|
35
|
+
load_adapter this_dir + '/../activerdf-rdflite/lib/activerdf_rdflite/fetching'
|
36
|
+
load_adapter this_dir + '/../activerdf-rdflite/lib/activerdf_rdflite/suggesting'
|
39
37
|
load_adapter this_dir + '/../activerdf-redland/lib/activerdf_redland/redland'
|
40
38
|
load_adapter this_dir + '/../activerdf-sparql/lib/activerdf_sparql/sparql'
|
41
39
|
load_adapter this_dir + '/../activerdf-yars/lib/activerdf_yars/jars2'
|
42
40
|
else
|
43
41
|
#we are indeed running as a gem
|
44
42
|
require 'gem_plugin'
|
45
|
-
$
|
43
|
+
$activerdflog.info 'ActiveRDF is installed as a Gem'
|
46
44
|
GemPlugin::Manager.instance.load "activerdf" => GemPlugin::INCLUDE
|
47
45
|
end
|
48
46
|
|
data/lib/active_rdf_helpers.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
#
|
1
|
+
# Default ActiveRDF error
|
2
2
|
class ActiveRdfError < StandardError
|
3
3
|
end
|
4
4
|
|
5
|
-
# adding bool_accessor to ruby
|
6
5
|
class Module
|
6
|
+
# Adds boolean accessor to a class (e.g. person.male?)
|
7
7
|
def bool_accessor *syms
|
8
8
|
attr_accessor(*syms)
|
9
9
|
syms.each { |sym| alias_method "#{sym}?", sym }
|
data/lib/active_rdf_log.rb
CHANGED
@@ -1,27 +1,16 @@
|
|
1
1
|
require 'logger'
|
2
2
|
|
3
|
-
$
|
4
|
-
|
5
|
-
|
6
|
-
RAILS_DEFAULT_LOGGER
|
7
|
-
rescue NameError
|
8
|
-
unless ENV['ACTIVE_RDF_LOG'].nil?
|
9
|
-
# write to environment variable $RDF_LOG if set
|
10
|
-
Logger.new(ENV['ACTIVE_RDF_LOG'], 1, 100*1024)
|
11
|
-
else
|
12
|
-
require 'tmpdir'
|
13
|
-
# else just write to the temp dir
|
14
|
-
Logger.new(Dir.tmpdir.to_s + "/activerdf.log", 1, 100*1024);
|
15
|
-
end
|
16
|
-
end
|
3
|
+
# use either $ACTIVE_RDF_LOG for logging or current directory
|
4
|
+
location = ENV['ACTIVE_RDF_LOG'] || "#{Dir.pwd}/activerdf.log"
|
5
|
+
$activerdflog = Logger.new(location, 1, 100*1024)
|
17
6
|
|
18
7
|
# if user has specified loglevel we use that, otherwise we use default level
|
19
8
|
# in the environment variable ACTIVE_RDF_LOG_LEVEL we expect numbers, which we
|
20
9
|
# have to convert
|
21
10
|
if ENV['ACTIVE_RDF_LOG_LEVEL'].nil?
|
22
|
-
$
|
11
|
+
$activerdflog.level = Logger::WARN
|
23
12
|
else
|
24
|
-
$
|
13
|
+
$activerdflog.level = ENV['ACTIVE_RDF_LOG_LEVEL'].to_i
|
25
14
|
end
|
26
15
|
|
27
16
|
class Logger
|
@@ -28,25 +28,25 @@ class TestResourceReading < Test::Unit::TestCase
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def test_eyal_predicates
|
31
|
-
predicates
|
32
|
-
|
33
|
-
|
34
|
-
assert_equal 3, predicates.size
|
35
|
-
predicates_labels = predicates.collect {|pred| pred.label }
|
31
|
+
# assert that eyal's three direct predicates are eye, age, and type
|
32
|
+
preds = @eyal.direct_predicates.collect {|p| p.uri }
|
33
|
+
assert_equal 3, preds.size
|
36
34
|
['age', 'eye', 'type'].each do |pr|
|
37
|
-
assert
|
35
|
+
assert preds.any? {|uri| uri =~ /.*#{pr}$/ }, "Eyal should have predicate #{pr}"
|
38
36
|
end
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
# test class level predicates
|
39
|
+
class_preds = @eyal.class_level_predicates.collect {|p| p.uri }
|
40
|
+
# eyal.type: person and resource, has predicates age, eye, and
|
41
|
+
# rdfs:label, rdfs:comment, etc.
|
42
|
+
assert_equal 10, class_preds.size
|
45
43
|
end
|
46
44
|
|
47
45
|
def test_eyal_types
|
48
|
-
|
49
|
-
|
46
|
+
types = @eyal.type
|
47
|
+
assert_equal 2, types.size
|
48
|
+
assert types.include?(AR::Person)
|
49
|
+
assert types.include?(RDFS::Resource)
|
50
50
|
end
|
51
51
|
|
52
52
|
def test_eyal_age
|
@@ -15,13 +15,9 @@ class TestQuery < Test::Unit::TestCase
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_sparql_generation
|
18
|
-
|
19
|
-
# TODO: write tests for distinct, ask
|
20
|
-
|
21
18
|
query = Query.new
|
22
19
|
query.select(:s)
|
23
20
|
query.where(:s, RDFS::Resource.new('predicate'), '30')
|
24
|
-
|
25
21
|
generated = Query2SPARQL.translate(query)
|
26
22
|
expected = "SELECT ?s WHERE { ?s <predicate> \"30\" . }"
|
27
23
|
assert_equal expected, generated
|
@@ -33,13 +29,15 @@ class TestQuery < Test::Unit::TestCase
|
|
33
29
|
generated = Query2SPARQL.translate(query)
|
34
30
|
expected = "SELECT ?s WHERE { ?s <foaf:age> ?a. ?a <rdf:type> <xsd:int> . }"
|
35
31
|
assert_equal expected, generated
|
32
|
+
end
|
36
33
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
34
|
+
def test_sparql_distinct
|
35
|
+
query = Query.new
|
36
|
+
query.distinct(:s)
|
37
|
+
query.where(:s, RDFS::Resource.new('foaf:age'), :a)
|
38
|
+
generated = Query2SPARQL.translate(query)
|
39
|
+
expected = "SELECT DISTINCT ?s WHERE { ?s <foaf:age> ?a . }"
|
40
|
+
assert_equal expected, generated
|
43
41
|
end
|
44
42
|
|
45
43
|
def test_query_omnipotent
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: activerdf
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: "1.
|
7
|
-
date: 2006-
|
6
|
+
version: "1.1"
|
7
|
+
date: 2006-12-08 00:00:00 +00:00
|
8
8
|
summary: Offers object-oriented access to RDF (with adapters to several datastores).
|
9
9
|
require_paths:
|
10
10
|
- lib
|