activerdf 1.6.6 → 1.6.8
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/CHANGELOG +9 -0
- data/lib/active_rdf.rb +14 -4
- data/lib/active_rdf/objectmanager/bnode.rb +1 -1
- data/lib/active_rdf/objectmanager/namespace.rb +3 -5
- data/lib/active_rdf/objectmanager/object_manager.rb +1 -1
- data/lib/active_rdf/objectmanager/resource.rb +10 -3
- data/lib/active_rdf/queryengine/query2sparql.rb +9 -5
- data/test/common.rb +35 -29
- data/test/objectmanager/test_literal.rb +1 -1
- data/test/objectmanager/test_namespace.rb +2 -2
- data/test/objectmanager/test_object_manager.rb +19 -14
- data/test/queryengine/test_query.rb +3 -3
- data/test/queryengine/test_query2jars2.rb +2 -2
- data/test/queryengine/test_query2sparql.rb +2 -2
- data/test/queryengine/test_query_engine.rb +2 -2
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
== activerdf (1.6.8) Thu, 01 Nov 2007 15:55:38 +0100
|
2
|
+
* no need for ObjectManager.construct_classes (done during namespace registration)
|
3
|
+
* small fix to running form source on jruby
|
4
|
+
* added spaces to query2sparql query creation
|
5
|
+
* sparql queries with a context now have a graph keyword
|
6
|
+
|
7
|
+
== activerdf (1.6.7) Fri, 21 Sep 2007 12:56:09 +0100
|
8
|
+
* updated bnode handling to SPARQL spec
|
9
|
+
|
1
10
|
== activerdf (1.6.6) Fri, 21 Sep 2007 10:06:17 +0100
|
2
11
|
* parse bnodes in SPARQL queries
|
3
12
|
* bugfix when using RDFS classes in queries
|
data/lib/active_rdf.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
|
1
4
|
# ActiveRDF loader
|
2
5
|
|
6
|
+
# determine the directory in which we are running depending on cruby or jruby
|
3
7
|
if RUBY_PLATFORM =~ /java/
|
4
8
|
# jruby can not follow symlinks, because java does not know the symlink concept
|
5
9
|
this_dir = File.dirname(File.expand_path(__FILE__))
|
@@ -7,9 +11,15 @@ else
|
|
7
11
|
file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
8
12
|
this_dir = File.dirname(File.expand_path(file))
|
9
13
|
end
|
10
|
-
$: << this_dir + '/'
|
11
|
-
$: << this_dir + '/active_rdf/'
|
12
14
|
|
15
|
+
# set the load path, which uses the running directory, but has to be different if running on jruby directly from source.
|
16
|
+
if RUBY_PLATFORM =~ /java/ and Gem::cache.search(/^activerdf$/).empty?
|
17
|
+
$: << this_dir + '/activerdf/lib/'
|
18
|
+
$: << this_dir + '/activerdf/lib/active_rdf/'
|
19
|
+
else
|
20
|
+
$: << this_dir + '/'
|
21
|
+
$: << this_dir + '/active_rdf/'
|
22
|
+
end
|
13
23
|
|
14
24
|
require 'active_rdf_helpers'
|
15
25
|
require 'active_rdf_log'
|
@@ -33,7 +43,7 @@ def load_adapter s
|
|
33
43
|
end
|
34
44
|
end
|
35
45
|
|
36
|
-
|
46
|
+
|
37
47
|
# determine whether activerdf is installed as a gem:
|
38
48
|
if Gem::cache.search(/^activerdf$/).empty?
|
39
49
|
# we are not running as a gem
|
@@ -48,7 +58,7 @@ if Gem::cache.search(/^activerdf$/).empty?
|
|
48
58
|
load_adapter this_dir + '/../activerdf-rdflite/lib/activerdf_rdflite/suggesting'
|
49
59
|
load_adapter this_dir + '/../activerdf-redland/lib/activerdf_redland/redland'
|
50
60
|
load_adapter this_dir + '/../activerdf-sparql/lib/activerdf_sparql/sparql'
|
51
|
-
load_adapter this_dir + '/../activerdf-yars/lib/activerdf_yars/jars2'
|
61
|
+
#load_adapter this_dir + '/../activerdf-yars/lib/activerdf_yars/jars2'
|
52
62
|
end
|
53
63
|
|
54
64
|
else
|
@@ -32,7 +32,7 @@ class Namespace
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def const_missing(klass)
|
35
|
-
Namespace.lookup(self.to_s.downcase.to_sym, klass)
|
35
|
+
ObjectManager.construct_class(Namespace.lookup(self.to_s.downcase.to_sym, klass))
|
36
36
|
end
|
37
37
|
|
38
38
|
# make some builtin methods private because lookup doesn't work otherwise
|
@@ -80,11 +80,9 @@ class Namespace
|
|
80
80
|
|
81
81
|
# returns local-part of URI
|
82
82
|
def self.localname(resource)
|
83
|
+
raise ActiveRdfError, "localname called on something that doesn't respond to uri" unless resource.respond_to? :uri
|
83
84
|
# get string representation of resource uri
|
84
|
-
uri =
|
85
|
-
when RDFS::Resource: resource.uri
|
86
|
-
else resource.to_s
|
87
|
-
end
|
85
|
+
uri = resource.uri
|
88
86
|
|
89
87
|
delimiter = uri.rindex(/#|\//)
|
90
88
|
if delimiter.nil? or delimiter == uri.size-1
|
@@ -75,7 +75,7 @@ class ObjectManager
|
|
75
75
|
# otherwise: create it, inside that module, as subclass of RDFS::Resource
|
76
76
|
# (using toplevel Class.new to prevent RDFS::Class.new from being called)
|
77
77
|
klass = _module.module_eval("#{klassname} = Object::Class.new(RDFS::Resource)")
|
78
|
-
klass.class_uri =
|
78
|
+
klass.class_uri = resource
|
79
79
|
klass
|
80
80
|
end
|
81
81
|
end
|
@@ -25,8 +25,13 @@ module RDFS
|
|
25
25
|
# creates new resource representing an RDF resource
|
26
26
|
def initialize uri
|
27
27
|
@uri = case uri
|
28
|
+
# allow Resource.new(other_resource)
|
28
29
|
when RDFS::Resource
|
29
30
|
uri.uri
|
31
|
+
# allow Resource.new(<uri>) by stripping out <>
|
32
|
+
when /^<([^>]*)>$/
|
33
|
+
$1
|
34
|
+
# allow Resource.new('uri')
|
30
35
|
when String
|
31
36
|
uri
|
32
37
|
else
|
@@ -44,6 +49,7 @@ module RDFS
|
|
44
49
|
def self.==(other)
|
45
50
|
other.respond_to?(:uri) ? other.uri == self.uri : false
|
46
51
|
end
|
52
|
+
def self.localname; Namespace.localname(self); end
|
47
53
|
|
48
54
|
##### ######
|
49
55
|
##### start of instance-level code
|
@@ -372,9 +378,10 @@ module RDFS
|
|
372
378
|
"<#{uri}>"
|
373
379
|
end
|
374
380
|
|
375
|
-
|
376
|
-
|
377
|
-
|
381
|
+
## TODO: ensure that we don't use this anywhere before removing it!!
|
382
|
+
#def Resource.to_s
|
383
|
+
# "<#{uri}>"
|
384
|
+
#end
|
378
385
|
|
379
386
|
private
|
380
387
|
|
@@ -14,10 +14,10 @@ class Query2SPARQL
|
|
14
14
|
|
15
15
|
str << "SELECT #{distinct}#{select_clauses.join(' ')} "
|
16
16
|
str << "WHERE { #{where_clauses(query)} #{filter_clauses(query)}} "
|
17
|
-
str << "LIMIT #{query.limits}" if query.limits
|
18
|
-
str << "OFFSET #{query.offsets}" if query.offsets
|
17
|
+
str << "LIMIT #{query.limits} " if query.limits
|
18
|
+
str << "OFFSET #{query.offsets} " if query.offsets
|
19
19
|
elsif query.ask?
|
20
|
-
str << "ASK { #{where_clauses(query)} }"
|
20
|
+
str << "ASK { #{where_clauses(query)} } "
|
21
21
|
end
|
22
22
|
|
23
23
|
return str
|
@@ -45,8 +45,12 @@ class Query2SPARQL
|
|
45
45
|
end
|
46
46
|
|
47
47
|
where_clauses = query.where_clauses.collect do |s,p,o,c|
|
48
|
-
|
49
|
-
|
48
|
+
# does there where clause use a context ?
|
49
|
+
if c.nil?
|
50
|
+
[s,p,o].collect {|term| construct_clause(term) }.join(' ')
|
51
|
+
else
|
52
|
+
"GRAPH #{construct_clause(c)} { #{construct_clause(s)} #{construct_clause(p)} #{construct_clause(o)} }"
|
53
|
+
end
|
50
54
|
end
|
51
55
|
|
52
56
|
"#{where_clauses.join(' . ')} ."
|
data/test/common.rb
CHANGED
@@ -1,25 +1,24 @@
|
|
1
1
|
def get_adapter
|
2
2
|
types = ConnectionPool.adapter_types
|
3
3
|
if types.include?(:rdflite)
|
4
|
-
|
4
|
+
get_rdflite
|
5
5
|
elsif types.include?(:redland)
|
6
|
-
|
6
|
+
get_redland
|
7
7
|
elsif types.include?(:sparql)
|
8
|
-
|
8
|
+
get_sparql
|
9
9
|
elsif types.include?(:yars)
|
10
|
-
|
10
|
+
get_yars
|
11
11
|
elsif types.include?(:jars2)
|
12
|
-
|
12
|
+
get_jars2
|
13
13
|
else
|
14
14
|
raise ActiveRdfError, "no suitable adapter found for test"
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
def get_read_only_adapter
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
else
|
19
|
+
if ConnectionPool.adapter_types.include?(:sparql)
|
20
|
+
get_sparql
|
21
|
+
else
|
23
22
|
raise ActiveRdfError, "no suitable read-only adapter found for test"
|
24
23
|
end
|
25
24
|
end
|
@@ -31,16 +30,16 @@ def get_different_adapter(existing_adapter)
|
|
31
30
|
if existing_adapter.class == RDFLite
|
32
31
|
ConnectionPool.add :type => :rdflite, :unique => true
|
33
32
|
else
|
34
|
-
|
33
|
+
get_rdflite
|
35
34
|
end
|
36
35
|
elsif types.include?(:redland) and existing_adapter.class != RedlandAdapter
|
37
|
-
|
36
|
+
get_rdflite
|
38
37
|
elsif types.include?(:sparql) and existing_adapter.class != SparqlAdapter
|
39
|
-
|
38
|
+
get_sparql
|
40
39
|
elsif types.include?(:yars) and existing_adapter.class != YarsAdapter
|
41
|
-
|
40
|
+
get_yars
|
42
41
|
elsif types.include?(:jars2) and existing_adapter.class != Jars2Adapter
|
43
|
-
|
42
|
+
get_jars2
|
44
43
|
else
|
45
44
|
raise ActiveRdfError, "only one adapter on this system, or no suitable adapter found for test"
|
46
45
|
end
|
@@ -48,32 +47,26 @@ end
|
|
48
47
|
|
49
48
|
def get_all_read_adapters
|
50
49
|
types = ConnectionPool.adapter_types
|
51
|
-
adapters = types.collect {|type|
|
52
|
-
if type == :sparql
|
53
|
-
ConnectionPool.add(:type => :sparql, :url => "http://m3pe.org:8080/repositories/test-people", :results => :sparql_xml)
|
54
|
-
else
|
55
|
-
ConnectionPool.add :type => type
|
56
|
-
end
|
57
|
-
}
|
50
|
+
adapters = types.collect {|type| self.send("get_#{type}") }
|
58
51
|
adapters.select {|adapter| adapter.reads?}
|
59
52
|
end
|
60
53
|
|
61
54
|
def get_all_write_adapters
|
62
55
|
types = ConnectionPool.adapter_types
|
63
|
-
adapters = types.collect {|type|
|
56
|
+
adapters = types.collect {|type| self.send("get_#{type}") }
|
64
57
|
adapters.select {|adapter| adapter.writes?}
|
65
58
|
end
|
66
59
|
|
67
60
|
def get_write_adapter
|
68
61
|
types = ConnectionPool.adapter_types
|
69
62
|
if types.include?(:rdflite)
|
70
|
-
|
63
|
+
get_rdflite
|
71
64
|
elsif types.include?(:redland)
|
72
|
-
|
65
|
+
get_redland
|
73
66
|
elsif types.include?(:yars)
|
74
|
-
|
67
|
+
get_yars
|
75
68
|
elsif types.include?(:jars2)
|
76
|
-
|
69
|
+
get_jars2
|
77
70
|
else
|
78
71
|
raise ActiveRdfError, "no suitable adapter found for test"
|
79
72
|
end
|
@@ -86,13 +79,26 @@ def get_different_write_adapter(existing_adapter)
|
|
86
79
|
if existing_adapter.class == RDFLite
|
87
80
|
ConnectionPool.add :type => :rdflite, :unique => true
|
88
81
|
else
|
89
|
-
|
82
|
+
get_rdflite
|
90
83
|
end
|
91
84
|
elsif types.include?(:redland) and existing_adapter.class != RedlandAdapter
|
92
|
-
|
85
|
+
get_redland
|
93
86
|
elsif types.include?(:yars) and existing_adapter.class != YarsAdapter
|
94
|
-
|
87
|
+
get_yars
|
95
88
|
else
|
96
89
|
raise ActiveRdfError, "only one write adapter on this system, or no suitable write adapter found for test"
|
97
90
|
end
|
98
91
|
end
|
92
|
+
|
93
|
+
private
|
94
|
+
def get_sparql
|
95
|
+
ConnectionPool.add(:type => :sparql, :url => "http://sparql.org/books",
|
96
|
+
:engine => :joseki, :results => :sparql_xml)
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_fetching; ConnectionPool.add(:type => :fetching); end
|
100
|
+
def get_suggesting; ConnectionPool.add(:type => :suggesting); end
|
101
|
+
def get_rdflite; ConnectionPool.add(:type => :rdflite); end
|
102
|
+
def get_redland; ConnectionPool.add(:type => :redland); end
|
103
|
+
def get_yars; ConnectionPool.add(:type => :yars); end
|
104
|
+
def get_jars2; ConnectionPool.add(:type => :jars2); end
|
@@ -53,10 +53,10 @@ class TestNamespace < Test::Unit::TestCase
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def test_class_localname
|
56
|
-
assert_equal 'type', Namespace.
|
56
|
+
assert_equal 'type', Namespace.lookup(:rdf, :type).localname
|
57
57
|
assert_equal 'type', RDF::type.localname
|
58
58
|
|
59
|
-
assert_equal 'Class', Namespace.
|
59
|
+
assert_equal 'Class', Namespace.lookup(:rdfs, :Class).localname
|
60
60
|
assert_equal 'Class', RDFS::Class.localname
|
61
61
|
end
|
62
62
|
|
@@ -20,6 +20,8 @@ class TestObjectManager < Test::Unit::TestCase
|
|
20
20
|
r1 = RDFS::Resource.new('abc')
|
21
21
|
r2 = RDFS::Resource.new('cde')
|
22
22
|
r3 = RDFS::Resource.new('cde')
|
23
|
+
assert_equal r3, RDFS::Resource.new(r3)
|
24
|
+
assert_equal r3, RDFS::Resource.new(r3.to_s)
|
23
25
|
|
24
26
|
assert_equal 'abc', r1.uri
|
25
27
|
assert_equal 'cde', r2.uri
|
@@ -31,24 +33,27 @@ class TestObjectManager < Test::Unit::TestCase
|
|
31
33
|
adapter.load "#{File.dirname(__FILE__)}/../test_person_data.nt"
|
32
34
|
|
33
35
|
Namespace.register(:test, 'http://activerdf.org/test/')
|
34
|
-
ObjectManager.construct_classes
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
assert_equal RDFS::Resource.new('http://activerdf.org/test/Person'), TEST::Person
|
38
|
+
assert_kind_of Class, TEST::Person
|
39
|
+
assert TEST::Person.ancestors.include?(RDFS::Resource)
|
40
|
+
assert_instance_of TEST::Person, TEST::Person.new('')
|
41
|
+
assert TEST::Person.new('').respond_to?(:uri)
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
assert_equal RDFS::Resource.new('http://www.w3.org/2000/01/rdf-schema#Class'), RDFS::Class
|
44
|
+
assert RDFS::Class.ancestors.include?(RDFS::Resource)
|
45
|
+
assert_kind_of Class, RDFS::Class
|
46
|
+
assert_instance_of RDFS::Resource, RDFS::Class.new('')
|
47
|
+
assert RDFS::Class.new('').respond_to?(:uri)
|
48
|
+
end
|
45
49
|
|
50
|
+
def test_custom_code
|
46
51
|
Namespace.register(:test, 'http://activerdf.org/test/')
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
|
53
|
+
TEST::Person.module_eval{ def hello; 'world'; end }
|
54
|
+
assert_respond_to TEST::Person.new(''), :hello
|
55
|
+
assert_equal 'world', TEST::Person.new('').hello
|
56
|
+
end
|
52
57
|
|
53
58
|
def test_class_uri
|
54
59
|
adapter = get_write_adapter
|
@@ -19,7 +19,7 @@ class TestQuery < Test::Unit::TestCase
|
|
19
19
|
query.select(:s)
|
20
20
|
query.where(:s, RDFS::Resource.new('predicate'), '30')
|
21
21
|
generated = Query2SPARQL.translate(query)
|
22
|
-
expected = "SELECT ?s WHERE { ?s <predicate> \"30\" . }"
|
22
|
+
expected = "SELECT ?s WHERE { ?s <predicate> \"30\" . } "
|
23
23
|
assert_equal expected, generated
|
24
24
|
|
25
25
|
query = Query.new
|
@@ -27,7 +27,7 @@ class TestQuery < Test::Unit::TestCase
|
|
27
27
|
query.where(:s, RDFS::Resource.new('foaf:age'), :a)
|
28
28
|
query.where(:a, RDFS::Resource.new('rdf:type'), RDFS::Resource.new('xsd:int'))
|
29
29
|
generated = Query2SPARQL.translate(query)
|
30
|
-
expected = "SELECT ?s WHERE { ?s <foaf:age> ?a. ?a <rdf:type> <xsd:int> . }"
|
30
|
+
expected = "SELECT ?s WHERE { ?s <foaf:age> ?a . ?a <rdf:type> <xsd:int> . } "
|
31
31
|
assert_equal expected, generated
|
32
32
|
end
|
33
33
|
|
@@ -36,7 +36,7 @@ class TestQuery < Test::Unit::TestCase
|
|
36
36
|
query.distinct(:s)
|
37
37
|
query.where(:s, RDFS::Resource.new('foaf:age'), :a)
|
38
38
|
generated = Query2SPARQL.translate(query)
|
39
|
-
expected = "SELECT DISTINCT ?s WHERE { ?s <foaf:age> ?a . }"
|
39
|
+
expected = "SELECT DISTINCT ?s WHERE { ?s <foaf:age> ?a . } "
|
40
40
|
assert_equal expected, generated
|
41
41
|
end
|
42
42
|
|
@@ -23,7 +23,7 @@ class TestQuery2Jars2 < Test::Unit::TestCase
|
|
23
23
|
query.where(:s, RDFS::Resource.new('predicate'), '30')
|
24
24
|
|
25
25
|
generated = Query2SPARQL.translate(query)
|
26
|
-
expected = "SELECT ?s WHERE { ?s <predicate> \"30\" . }"
|
26
|
+
expected = "SELECT ?s WHERE { ?s <predicate> \"30\" . } "
|
27
27
|
assert_equal expected, generated
|
28
28
|
|
29
29
|
query = Query.new
|
@@ -31,7 +31,7 @@ class TestQuery2Jars2 < Test::Unit::TestCase
|
|
31
31
|
query.where(:s, RDFS::Resource.new('foaf:age'), :a)
|
32
32
|
query.where(:a, RDFS::Resource.new('rdf:type'), RDFS::Resource.new('xsd:int'))
|
33
33
|
generated = Query2SPARQL.translate(query)
|
34
|
-
expected = "SELECT ?s WHERE { ?s <foaf:age> ?a. ?a <rdf:type> <xsd:int> . }"
|
34
|
+
expected = "SELECT ?s WHERE { ?s <foaf:age> ?a . ?a <rdf:type> <xsd:int> . } "
|
35
35
|
assert_equal expected, generated
|
36
36
|
|
37
37
|
# query = Query.new
|
@@ -23,7 +23,7 @@ class TestQuery2Sparql < Test::Unit::TestCase
|
|
23
23
|
query.where(:s, RDFS::Resource.new('predicate'), '30')
|
24
24
|
|
25
25
|
generated = Query2SPARQL.translate(query)
|
26
|
-
expected = "SELECT ?s WHERE { ?s <predicate> \"30\" . }"
|
26
|
+
expected = "SELECT ?s WHERE { ?s <predicate> \"30\" . } "
|
27
27
|
assert_equal expected, generated
|
28
28
|
|
29
29
|
query = Query.new
|
@@ -31,7 +31,7 @@ class TestQuery2Sparql < Test::Unit::TestCase
|
|
31
31
|
query.where(:s, RDFS::Resource.new('foaf:age'), :a)
|
32
32
|
query.where(:a, RDFS::Resource.new('rdf:type'), RDFS::Resource.new('xsd:int'))
|
33
33
|
generated = Query2SPARQL.translate(query)
|
34
|
-
expected = "SELECT ?s WHERE { ?s <foaf:age> ?a. ?a <rdf:type> <xsd:int> . }"
|
34
|
+
expected = "SELECT ?s WHERE { ?s <foaf:age> ?a . ?a <rdf:type> <xsd:int> . } "
|
35
35
|
assert_equal expected, generated
|
36
36
|
|
37
37
|
# query = Query.new
|
@@ -24,7 +24,7 @@ class TestQueryEngine < Test::Unit::TestCase
|
|
24
24
|
query.where(:s, RDFS::Resource.new('predicate'), '30')
|
25
25
|
|
26
26
|
generated = Query2SPARQL.translate(query)
|
27
|
-
expected = "SELECT ?s WHERE { ?s <predicate> \"30\" . }"
|
27
|
+
expected = "SELECT ?s WHERE { ?s <predicate> \"30\" . } "
|
28
28
|
assert_equal expected, generated
|
29
29
|
|
30
30
|
query = Query.new
|
@@ -32,7 +32,7 @@ class TestQueryEngine < Test::Unit::TestCase
|
|
32
32
|
query.where(:s, RDFS::Resource.new('foaf:age'), :a)
|
33
33
|
query.where(:a, RDFS::Resource.new('rdf:type'), RDFS::Resource.new('xsd:int'))
|
34
34
|
generated = Query2SPARQL.translate(query)
|
35
|
-
expected = "SELECT ?s WHERE { ?s <foaf:age> ?a. ?a <rdf:type> <xsd:int> . }"
|
35
|
+
expected = "SELECT ?s WHERE { ?s <foaf:age> ?a . ?a <rdf:type> <xsd:int> . } "
|
36
36
|
assert_equal expected, generated
|
37
37
|
|
38
38
|
# query = Query.new
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: activerdf
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.6.
|
7
|
-
date: 2007-
|
6
|
+
version: 1.6.8
|
7
|
+
date: 2007-11-01 00:00:00 +01:00
|
8
8
|
summary: Offers object-oriented access to RDF (with adapters to several datastores).
|
9
9
|
require_paths:
|
10
10
|
- lib
|