rdf-agraph 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,29 @@
1
+ class RDF::AllegroGraph::Query
2
+ # A literal value which can be passed as an argument to a Prolog functor.
3
+ #
4
+ # @see @FunctorExpression
5
+ class PrologLiteral
6
+ # Constract a new Prolog literal.
7
+ #
8
+ # @param [Object] value A Ruby value.
9
+ def initialize(value)
10
+ @value = value
11
+ end
12
+
13
+ # Serialize this literal as a string. We need to be careful about
14
+ # security here: Our callers might try to pass in untrustworthy values
15
+ # without thinking through the consequences, and we want to limit the
16
+ # damage. We assume that all symbols are trustworthy.
17
+ #
18
+ # @return [String]
19
+ def to_s
20
+ case @value
21
+ when Symbol, Numeric
22
+ @value.to_s
23
+ else
24
+ err = "Don't know how to serialize #{@value.inspect} securely"
25
+ raise ArgumentError.new(err)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,124 @@
1
+ module RDF::AllegroGraph
2
+
3
+ # A query with AllegroGraph-specific extensions.
4
+ #
5
+ # Note that many of of the more exotic features of this class can only be
6
+ # used when running Prolog queries against a Session object. This
7
+ # requires both elevated AllegroGraph privileges and dedicated back-end
8
+ # session resources on the server, so plan accordingly.
9
+ #
10
+ # The Functors module contains a wide variety of functors which may be
11
+ # used when building a Prolog query.
12
+ #
13
+ # @see AbstractRepository#build_query
14
+ # @see Functors
15
+ class Query < RDF::Query
16
+ autoload :PrologLiteral, 'rdf/allegro_graph/query/prolog_literal'
17
+ autoload :FunctorExpression, 'rdf/allegro_graph/query/functor_expression'
18
+
19
+ # Include our APIs.
20
+ include Functors::SnaFunctors
21
+
22
+ # Create a new query.
23
+ # @private
24
+ def initialize(repository, &block)
25
+ @repository = repository
26
+ super(&block)
27
+ end
28
+
29
+ # Run this query against the associated repository. This method exists
30
+ # solely to make the following API pleasant to use:
31
+ #
32
+ # repo.build_query do |q|
33
+ # q.pattern [:s, :p, :o]
34
+ # end.run do |solution|
35
+ # puts solution
36
+ # end
37
+ #
38
+ # Note that this function returns an Enumerator, not an array, because
39
+ # RDF.rb is committed to streaming results gradually. If you want to
40
+ # treat the result as an array, call 'to_a' explicitly:
41
+ #
42
+ # solutions = repo.build_query do |q|
43
+ # q.pattern [:s, :p, :o]
44
+ # end.run.to_a
45
+ #
46
+ # If you forget to do this, you will run a new query each time you
47
+ # attempt to iterate over the solutions!
48
+ #
49
+ # @overload run
50
+ # @return [Enumerator<RDF::Query::Solution>]
51
+ #
52
+ # @overload run
53
+ # @yield solution
54
+ # @yieldparam [RDF::Query::Solution]
55
+ # @yieldreturn [void]
56
+ # @return [void]
57
+ #
58
+ # @see Repository#query
59
+ # @note This function returns a single-use Enumerator! If you want to
60
+ # to treat the results as an array, call `to_a` on it, or you will
61
+ # re-run the query against the server repeatedly. This curious
62
+ # decision is made for consistency with RDF.rb.
63
+ def run(&block)
64
+ @repository.query(self, &block)
65
+ end
66
+
67
+ # Add a functor expression to this query. Functors can only be used
68
+ # in Prolog queries.
69
+ #
70
+ # @param [String] name
71
+ # @param [Array<Symbol,RDF::Value,value>] arguments
72
+ # The arguments to the functor, which may be either variables,
73
+ # RDF::Value objects, or Ruby values that we can convert to literals.
74
+ # @return [void]
75
+ def functor(name, *arguments)
76
+ # TODO: Don't abuse duck-typing quite so much.
77
+ patterns <<
78
+ RDF::AllegroGraph::Query::FunctorExpression.new(name, *arguments)
79
+ end
80
+
81
+ # Does this query contain Prolog-specific functors that we can't
82
+ # represent as SPARQL?
83
+ #
84
+ # @return [Boolean]
85
+ def requires_prolog?
86
+ !patterns.all? {|p| p.kind_of?(RDF::Query::Pattern) }
87
+ end
88
+
89
+ # Convert this query to AllegoGraph Prolog notation.
90
+ #
91
+ # @param [RDF::AllegroGraph::Repository] repository
92
+ # @return [String]
93
+ # @private
94
+ def to_prolog(repository)
95
+ variables = []
96
+ functors = []
97
+ patterns.each do |p|
98
+ # Extract any new variables we see in the query.
99
+ p.variables.each {|_,v| variables << v unless variables.include?(v) }
100
+ functors << convert_to_functor(p).to_prolog(repository)
101
+ end
102
+ "(select (#{variables.join(" ")})\n #{functors.join("\n ")})"
103
+ end
104
+
105
+ protected
106
+
107
+ # Convert patterns to functors (and leave functors unchanged).
108
+ #
109
+ # @param [RDF::Query::Pattern,FunctorExpression] pattern_or_functor
110
+ # @return [FunctorExpression]
111
+ # @private
112
+ def convert_to_functor(pattern_or_functor)
113
+ case pattern_or_functor
114
+ when FunctorExpression then pattern_or_functor
115
+ else
116
+ p = pattern_or_functor
117
+ if p.optional? || p.context
118
+ raise ArgumentError.new("Can't translate #{p} to Prolog functor")
119
+ end
120
+ FunctorExpression.new('q-', p.subject, p.predicate, p.object)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,81 @@
1
+ require 'pathname'
2
+
3
+ module RDF::AllegroGraph
4
+ # An AllegroGraph RDF repository.
5
+ #
6
+ # Note that this class does not interoperate well with the Unix `fork`
7
+ # command if you're using blank nodes. See README.md for details.
8
+ class Repository < AbstractRepository
9
+ # Create a new AllegroGraph repository adapter.
10
+ #
11
+ # @overload initialize(options)
12
+ # @param [Hash{Symbol => Object}] options
13
+ # @option options [Server] :server The server hosting the repository.
14
+ # @option options [String] :id The name of the repository.
15
+ # @option options [Boolean] :create Create the repository if necessary?
16
+ #
17
+ # @overload initialize(url, options)
18
+ # @param [String] url The URL of the repository.
19
+ # @param [Hash{Symbol => Object}] options
20
+ # @option options [Boolean] :create Create the repository if necessary?
21
+ def initialize(url_or_options, options={})
22
+ case url_or_options
23
+ when String
24
+ # TODO: Clean this up.
25
+ url = URI.parse(url_or_options)
26
+ path = Pathname.new(url.path)
27
+ url.path = path.parent.parent.to_s
28
+ server = Server.new(url.to_s).server
29
+ id = path.basename
30
+ else
31
+ server = url_or_options[:server].server
32
+ id = url_or_options[:id]
33
+ options = url_or_options
34
+ end
35
+ super(::AllegroGraph::Repository.new(server, id))
36
+ @repo.create_if_missing! if options[:create]
37
+ end
38
+
39
+ # Delete this repository if it exists.
40
+ #
41
+ # @return [void]
42
+ def delete!
43
+ @repo.delete!
44
+ end
45
+
46
+ # Create a new, persistent AllegroGraph session. If called without a
47
+ # block, simply returns the new session (and expects the caller to
48
+ # close it). If called with a block, automatically commits or rolls
49
+ # back the transaction, and closes the session.
50
+ #
51
+ # @overload session
52
+ # @return [Session] The newly created session. It's a good idea to
53
+ # close this manually; doing so frees up server resources.
54
+ # @see Session#commit
55
+ # @see Session#rollback
56
+ # @see Session#close
57
+ #
58
+ # @overload session
59
+ # @yield session
60
+ # @yieldparam [Session] session
61
+ # @yieldreturn [Object]
62
+ # @return [Object] The result returned from the block.
63
+ def session
64
+ if block_given?
65
+ session = Session.new(@repo)
66
+ begin
67
+ result = yield session
68
+ session.commit
69
+ result
70
+ rescue => e
71
+ session.rollback
72
+ raise
73
+ ensure
74
+ session.close
75
+ end
76
+ else
77
+ Session.new(@repo)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,93 @@
1
+ module RDF::AllegroGraph
2
+ # An AllegroGraph server containing several repositories. We attempt to
3
+ # mimic the public API of RDF::Sesame::Server for the sake of
4
+ # convenience, though we do not implement several internal methods, and
5
+ # we generally attempt to raise errors when we can't connect to the
6
+ # server, unlike RDF::Sesame::Server, which just returns default values.
7
+ #
8
+ # @see RDF::Sesame::Server
9
+ class Server
10
+ attr_reader :server # @private
11
+
12
+ # Create a new Server object.
13
+ #
14
+ # @param [String] url The Sesame URL of the AllegroGraph server.
15
+ def initialize(url="http://localhost:10035")
16
+ parsed = URI.parse(url)
17
+ options = {
18
+ :host => parsed.host, :post => parsed.port,
19
+ :username => parsed.user, :password => parsed.password
20
+ }
21
+ @server = AllegroGraph::Server.new(options)
22
+
23
+ unless parsed.path.nil? || parsed.path.empty? || parsed.path == "/"
24
+ err = "AllegroGraph URLs with paths not supported: #{url}"
25
+ raise ArgumentError.new(err)
26
+ end
27
+ end
28
+
29
+ # Get the protocol version supported by this server.
30
+ #
31
+ # @return [Integer]
32
+ def protocol
33
+ @server.request_http(:get, path(:protocol),
34
+ :expected_status_code => 200).to_i
35
+ end
36
+ alias_method :protocol_version, :protocol
37
+
38
+ # Return a hash table of all repositories on this server.
39
+ #
40
+ # @return [Hash<String,Repository>]
41
+ def repositories
42
+ result = {}
43
+ @server.request_json(:get, path(:repositories),
44
+ :expected_status_code => 200).each do |repo|
45
+ result[repo['id']] = Repository.new(:server => self, :id => repo['id'])
46
+ end
47
+ result
48
+ end
49
+
50
+ # Return true if the specified repository exists on the server.
51
+ #
52
+ # @param [String] id The name of the repository.
53
+ # @return [Boolean]
54
+ def has_repository?(id)
55
+ repositories.has_key?(id)
56
+ end
57
+
58
+ # Iterate over all repositories.
59
+ #
60
+ # @yield repository
61
+ # @yieldparam [Repository] repository
62
+ # @yieldreturn [void]
63
+ # @return [void]
64
+ def each_repository(&block)
65
+ repositories.values.each(&block)
66
+ end
67
+ alias_method :each, :each_repository
68
+
69
+ # Look up a specific repository by name, and optionally create it.
70
+ #
71
+ # @param [String] id The name of the repository.
72
+ # @param [Hash] options
73
+ # @option options [Boolean] :create
74
+ # If true, and the repository does not exist, create it.
75
+ # @return [Repository,nil]
76
+ # The repository, if it exists or was created, or nil otherwise.
77
+ def repository(id, options={})
78
+ result = repositories[id]
79
+ if result.nil? && options[:create]
80
+ result = Repository.new(:server => self, :id => id, :create => true)
81
+ end
82
+ result
83
+ end
84
+ alias_method :[], :repository
85
+
86
+ protected
87
+
88
+ # Generate a path to a resource on the server.
89
+ def path(relativate_path) # @private
90
+ "/#{relativate_path}"
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,78 @@
1
+ module RDF::AllegroGraph
2
+
3
+ # A persistent AllegroGraph session. This takes up more server resources
4
+ # than a normal stateless repository connection, but it allows access to
5
+ # advanced AllegroGraph features.
6
+ #
7
+ # Note that this class does not interoperate well with the Unix `fork`
8
+ # command if you're using blank nodes. See README.md for details.
9
+ #
10
+ # @see Repository#session
11
+ class Session < AbstractRepository
12
+ # Create a new session. This parameter takes an
13
+ # ::AllegroGraph::Repository object as an argument, so we've not going
14
+ # to document it publically.
15
+ #
16
+ # @private
17
+ def initialize(agraph_repo)
18
+ super(::AllegroGraph::Session.create(agraph_repo))
19
+ @last_unique_id = 0
20
+ end
21
+
22
+ # Explicitly close the current session and release all server resources.
23
+ # This function does _not_ commit any outstanding transactions.
24
+ #
25
+ # @return [void]
26
+ # @see #commit
27
+ # @see #rollback
28
+ def close
29
+ @repo.request_http(:post, path('session/close'),
30
+ :expected_status_code => 204)
31
+ end
32
+
33
+ # Commit the current changes to the main repository.
34
+ #
35
+ # @return [void]
36
+ # @see #rollback
37
+ def commit
38
+ @repo.commit
39
+ end
40
+
41
+ # Roll back the changes made since the last commit.
42
+ #
43
+ # @return [void]
44
+ # @see #commit
45
+ def rollback
46
+ @repo.rollback
47
+ end
48
+
49
+ # Define an SNA generator.
50
+ #
51
+ # @param [Hash] options
52
+ # @option options [RDF::Resource,Array<RDF::Resource>] :object_of
53
+ # Follow links defined by specified predicates.
54
+ # @option options [RDF::Resource,Array<RDF::Resource>] :subject_of
55
+ # Follow links defined by specified predicates, but from the object
56
+ # to the subject.
57
+ # @option options [RDF::Resource,Array<RDF::Resource>] :undirected
58
+ # Follow links defined by specified predicates in both directions.
59
+ # @return [Query::PrologLiteral]
60
+ # A value which may be used in Prolog queries.
61
+ #
62
+ # @see Query#ego_group_member
63
+ def generator(options)
64
+ id = unique_id
65
+ generator = SnaGenerator.new(self, options)
66
+ @repo.request_json(:put, path("snaGenerators/#{id}"),
67
+ :parameters => generator.to_params,
68
+ :expected_status_code => 204)
69
+ Query::PrologLiteral.new(id.to_sym)
70
+ end
71
+
72
+ protected
73
+
74
+ def unique_id
75
+ "id#{@last_unique_id += 1}"
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,37 @@
1
+ module RDF::AllegroGraph
2
+ # Internal helper class for defining SNA generators.
3
+ #
4
+ # @private
5
+ class SnaGenerator
6
+ attr_reader :options
7
+
8
+ def initialize(repository, options)
9
+ @repository = repository
10
+ @options = options
11
+ end
12
+
13
+ def to_params
14
+ params = {}
15
+ params.merge!(option_to_hash(:objectOf, :object_of))
16
+ params.merge!(option_to_hash(:subjectOf, :subject_of))
17
+ params.merge!(option_to_hash(:undirected, :undirected))
18
+ params
19
+ end
20
+
21
+ protected
22
+
23
+ def option_to_hash(param_name, option_name)
24
+ if @options.has_key?(option_name)
25
+ value = @options[option_name]
26
+ case value
27
+ when Array
28
+ { param_name => value.map {|v| @repository.serialize(v) } }
29
+ else
30
+ { param_name => @repository.serialize(value) }
31
+ end
32
+ else
33
+ {}
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ require 'rdf'
2
+ require 'agraph'
3
+ require 'enumerator'
4
+
5
+ # AllegroGraph integration for RDF.rb.
6
+ module RDF::AllegroGraph
7
+ autoload :Query, 'rdf/allegro_graph/query'
8
+ autoload :Server, 'rdf/allegro_graph/server'
9
+ autoload :AbstractRepository, 'rdf/allegro_graph/abstract_repository'
10
+ autoload :Repository, 'rdf/allegro_graph/repository'
11
+ autoload :SnaGenerator, 'rdf/allegro_graph/sna_generator'
12
+ autoload :Functors, 'rdf/allegro_graph/functors'
13
+ autoload :Session, 'rdf/allegro_graph/session'
14
+ end
data/lib/rdf-agraph.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'rdf/allegro_graph'
2
+
metadata ADDED
@@ -0,0 +1,227 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rdf-agraph
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
+ platform: ruby
12
+ authors:
13
+ - Eric Kidd
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-02-18 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rdf
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 17
30
+ segments:
31
+ - 0
32
+ - 3
33
+ - 1
34
+ version: 0.3.1
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: agraph
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 19
46
+ segments:
47
+ - 0
48
+ - 1
49
+ - 4
50
+ version: 0.1.4
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: json
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 9
62
+ segments:
63
+ - 0
64
+ - 5
65
+ - 1
66
+ version: 0.5.1
67
+ type: :runtime
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: rdf-spec
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ hash: 17
78
+ segments:
79
+ - 0
80
+ - 3
81
+ - 1
82
+ version: 0.3.1
83
+ type: :development
84
+ version_requirements: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ name: yard
87
+ prerelease: false
88
+ requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ hash: 7
94
+ segments:
95
+ - 0
96
+ - 6
97
+ - 0
98
+ version: 0.6.0
99
+ type: :development
100
+ version_requirements: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ name: BlueCloth
103
+ prerelease: false
104
+ requirement: &id006 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ hash: 21
110
+ segments:
111
+ - 1
112
+ - 0
113
+ - 1
114
+ version: 1.0.1
115
+ type: :development
116
+ version_requirements: *id006
117
+ - !ruby/object:Gem::Dependency
118
+ name: rspec
119
+ prerelease: false
120
+ requirement: &id007 !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ hash: 27
126
+ segments:
127
+ - 2
128
+ - 5
129
+ - 0
130
+ version: 2.5.0
131
+ type: :development
132
+ version_requirements: *id007
133
+ - !ruby/object:Gem::Dependency
134
+ name: rcov
135
+ prerelease: false
136
+ requirement: &id008 !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ hash: 41
142
+ segments:
143
+ - 0
144
+ - 9
145
+ - 9
146
+ version: 0.9.9
147
+ type: :development
148
+ version_requirements: *id008
149
+ - !ruby/object:Gem::Dependency
150
+ name: rake
151
+ prerelease: false
152
+ requirement: &id009 !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ hash: 49
158
+ segments:
159
+ - 0
160
+ - 8
161
+ - 7
162
+ version: 0.8.7
163
+ type: :development
164
+ version_requirements: *id009
165
+ description: An AllegroGraph adapter for use with RDF.rb.
166
+ email: rdf-agraph@kiddsoftware.com
167
+ executables: []
168
+
169
+ extensions: []
170
+
171
+ extra_rdoc_files: []
172
+
173
+ files:
174
+ - AUTHORS
175
+ - README.md
176
+ - UNLICENSE
177
+ - VERSION
178
+ - lib/rdf-agraph.rb
179
+ - lib/rdf/allegro_graph.rb
180
+ - lib/rdf/allegro_graph/server.rb
181
+ - lib/rdf/allegro_graph/functors.rb
182
+ - lib/rdf/allegro_graph/functors/sna_functors.rb
183
+ - lib/rdf/allegro_graph/repository.rb
184
+ - lib/rdf/allegro_graph/query.rb
185
+ - lib/rdf/allegro_graph/abstract_repository.rb
186
+ - lib/rdf/allegro_graph/query/functor_expression.rb
187
+ - lib/rdf/allegro_graph/query/prolog_literal.rb
188
+ - lib/rdf/allegro_graph/sna_generator.rb
189
+ - lib/rdf/allegro_graph/session.rb
190
+ has_rdoc: false
191
+ homepage: http://rdf-agraph.rubyforge.org/
192
+ licenses:
193
+ - Public Domain
194
+ post_install_message:
195
+ rdoc_options: []
196
+
197
+ require_paths:
198
+ - lib
199
+ required_ruby_version: !ruby/object:Gem::Requirement
200
+ none: false
201
+ requirements:
202
+ - - ">="
203
+ - !ruby/object:Gem::Version
204
+ hash: 57
205
+ segments:
206
+ - 1
207
+ - 8
208
+ - 7
209
+ version: 1.8.7
210
+ required_rubygems_version: !ruby/object:Gem::Requirement
211
+ none: false
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ hash: 3
216
+ segments:
217
+ - 0
218
+ version: "0"
219
+ requirements: []
220
+
221
+ rubyforge_project: rdf-agraph
222
+ rubygems_version: 1.5.2
223
+ signing_key:
224
+ specification_version: 3
225
+ summary: AllegroGraph adapter for RDF.rb
226
+ test_files: []
227
+