activerdf 1.0 → 1.1
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.
- 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
|