rdf 0.0.5 → 0.0.6
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 +49 -32
- data/VERSION +1 -1
- data/lib/rdf.rb +7 -0
- data/lib/rdf/cli.rb +1 -1
- data/lib/rdf/format.rb +95 -0
- data/lib/rdf/graph.rb +8 -0
- data/lib/rdf/literal.rb +66 -1
- data/lib/rdf/node.rb +34 -4
- data/lib/rdf/ntriples.rb +12 -0
- data/lib/rdf/ntriples/format.rb +13 -0
- data/lib/rdf/ntriples/reader.rb +87 -0
- data/lib/rdf/ntriples/writer.rb +50 -0
- data/lib/rdf/query.rb +212 -0
- data/lib/rdf/query/pattern.rb +152 -0
- data/lib/rdf/query/solution.rb +139 -0
- data/lib/rdf/query/variable.rb +145 -0
- data/lib/rdf/reader.rb +7 -31
- data/lib/rdf/reader/ntriples.rb +4 -85
- data/lib/rdf/repository.rb +3 -1
- data/lib/rdf/statement.rb +54 -11
- data/lib/rdf/uri.rb +19 -0
- data/lib/rdf/value.rb +53 -2
- data/lib/rdf/version.rb +1 -1
- data/lib/rdf/vocabulary.rb +17 -0
- data/lib/rdf/writer.rb +16 -38
- data/lib/rdf/writer/ntriples.rb +4 -49
- metadata +21 -2
@@ -0,0 +1,139 @@
|
|
1
|
+
module RDF class Query
|
2
|
+
##
|
3
|
+
# An RDF query solution.
|
4
|
+
#
|
5
|
+
# @example Iterating over every binding in the solution
|
6
|
+
# solution.each_binding { |name, value| puts value.inspect }
|
7
|
+
# solution.each_variable { |variable| puts variable.value.inspect }
|
8
|
+
#
|
9
|
+
# @example Iterating over every value in the solution
|
10
|
+
# solution.each_value { |value| puts value.inspect }
|
11
|
+
#
|
12
|
+
# @example Checking whether a variable is bound or unbound
|
13
|
+
# solution.bound?(:title)
|
14
|
+
# solution.unbound?(:mbox)
|
15
|
+
#
|
16
|
+
# @example Retrieving the value of a bound variable
|
17
|
+
# solution[:mbox]
|
18
|
+
# solution.mbox
|
19
|
+
#
|
20
|
+
# @example Retrieving all bindings in the solution as a `Hash`
|
21
|
+
# solution.to_hash #=> {:mbox => "jrhacker@example.org", ...}
|
22
|
+
#
|
23
|
+
class Solution
|
24
|
+
undef_method *(instance_methods - %w(__id__ __send__ __class__ __eval__ instance_eval inspect class object_id))
|
25
|
+
|
26
|
+
include Enumerable
|
27
|
+
|
28
|
+
##
|
29
|
+
# @param [Hash{Symbol => Value}] bindings
|
30
|
+
def initialize(bindings = {})
|
31
|
+
@bindings = bindings
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Enumerates over every variable binding in this solution.
|
36
|
+
#
|
37
|
+
# @yield [name, value]
|
38
|
+
# @yieldparam [Symbol, Value]
|
39
|
+
# @return [Enumerator]
|
40
|
+
def each_binding(&block)
|
41
|
+
@bindings.each(&block)
|
42
|
+
end
|
43
|
+
|
44
|
+
alias_method :each, :each_binding
|
45
|
+
|
46
|
+
##
|
47
|
+
# Enumerates over every variable name in this solution.
|
48
|
+
#
|
49
|
+
# @yield [name]
|
50
|
+
# @yieldparam [Symbol]
|
51
|
+
# @return [Enumerator]
|
52
|
+
def each_name(&block)
|
53
|
+
@bindings.each_key(&block)
|
54
|
+
end
|
55
|
+
|
56
|
+
alias_method :each_key, :each_name
|
57
|
+
|
58
|
+
##
|
59
|
+
# Enumerates over every variable value in this solution.
|
60
|
+
#
|
61
|
+
# @yield [value]
|
62
|
+
# @yieldparam [Value]
|
63
|
+
# @return [Enumerator]
|
64
|
+
def each_value(&block)
|
65
|
+
@bindings.each_value(&block)
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Enumerates over every variable in this solution.
|
70
|
+
#
|
71
|
+
# @yield [variable]
|
72
|
+
# @yieldparam [Variable]
|
73
|
+
# @return [Enumerator]
|
74
|
+
def each_variable(&block)
|
75
|
+
@bindings.each do |name, value|
|
76
|
+
block.call(Variable.new(name, value))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Returns `true` if the variable `name` is bound in this solution.
|
82
|
+
#
|
83
|
+
# @param [Symbol] name
|
84
|
+
# @return [Boolean]
|
85
|
+
def bound?(name)
|
86
|
+
!unbound?(name)
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Returns `true` if the variable `name` is unbound in this solution.
|
91
|
+
#
|
92
|
+
# @param [Symbol] name
|
93
|
+
# @return [Boolean]
|
94
|
+
def unbound?(name)
|
95
|
+
@bindings[name.to_sym].nil?
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Returns the value of the variable `name`.
|
100
|
+
#
|
101
|
+
# @param [Symbol] name
|
102
|
+
# @return [Value]
|
103
|
+
def [](name)
|
104
|
+
@bindings[name.to_sym]
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# @return [Array<Array(Symbol, Value)>}
|
109
|
+
def to_a
|
110
|
+
@bindings.to_a
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# @return [Hash{Symbol => Value}}
|
115
|
+
def to_hash
|
116
|
+
@bindings.dup
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# @return [String]
|
121
|
+
def inspect
|
122
|
+
sprintf("#<%s:%#0x(%s)>", self.class.name, object_id, @bindings.inspect)
|
123
|
+
end
|
124
|
+
|
125
|
+
protected
|
126
|
+
|
127
|
+
##
|
128
|
+
# @param [Symbol] name
|
129
|
+
# @return [Value]
|
130
|
+
def method_missing(name, *args, &block)
|
131
|
+
if args.empty? && @bindings.has_key?(name.to_sym)
|
132
|
+
@bindings[name.to_sym]
|
133
|
+
else
|
134
|
+
super
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module RDF class Query
|
2
|
+
##
|
3
|
+
# An RDF query variable.
|
4
|
+
#
|
5
|
+
# @example Creating an unbound variable
|
6
|
+
# var = RDF::Query::Variable.new(:x)
|
7
|
+
# var.unbound? #=> true
|
8
|
+
# var.value #=> nil
|
9
|
+
#
|
10
|
+
# @example Unbound variables match any value
|
11
|
+
# var === 42 #=> true
|
12
|
+
#
|
13
|
+
# @example Creating a bound variable
|
14
|
+
# var = RDF::Query::Variable.new(:y, 123)
|
15
|
+
# var.bound? #=> true
|
16
|
+
# var.value #=> 123
|
17
|
+
#
|
18
|
+
# @example Bound variables match only their actual value
|
19
|
+
# var === 42 #=> false
|
20
|
+
# var === 123 #=> true
|
21
|
+
#
|
22
|
+
# @example Getting the variable name
|
23
|
+
# var.named? #=> true
|
24
|
+
# var.name #=> :y
|
25
|
+
# var.to_sym #=> :y
|
26
|
+
#
|
27
|
+
# @example Rebinding a variable returns the previous value
|
28
|
+
# var.bind!(456) #=> 123
|
29
|
+
# var.value #=> 456
|
30
|
+
#
|
31
|
+
# @example Unbinding a previously bound variable
|
32
|
+
# var.unbind!
|
33
|
+
# var.unbound? #=> true
|
34
|
+
#
|
35
|
+
# @example Getting the string representation of a variable
|
36
|
+
# var = RDF::Query::Variable.new(:x)
|
37
|
+
# var.to_s #=> "?x"
|
38
|
+
# var = RDF::Query::Variable.new(:y, 123)
|
39
|
+
# var.to_s #=> "?y=123"
|
40
|
+
#
|
41
|
+
class Variable < Value
|
42
|
+
# @return [Symbol] The variable's name.
|
43
|
+
attr_accessor :name
|
44
|
+
|
45
|
+
alias_method :to_sym, :name
|
46
|
+
|
47
|
+
# @return [Value] The variable's value.
|
48
|
+
attr_accessor :value
|
49
|
+
|
50
|
+
##
|
51
|
+
# @param [Symbol] name
|
52
|
+
# @param [Value] value
|
53
|
+
def initialize(name, value = nil)
|
54
|
+
@name, @value = name.to_sym, value
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Returns `true` if this variable has a name.
|
59
|
+
#
|
60
|
+
# @return [Boolean]
|
61
|
+
def named?
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Returns `true` if this variable is bound.
|
67
|
+
#
|
68
|
+
# @return [Boolean]
|
69
|
+
def bound?
|
70
|
+
!unbound?
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Returns `true` if this variable is unbound.
|
75
|
+
#
|
76
|
+
# @return [Boolean]
|
77
|
+
def unbound?
|
78
|
+
value.nil?
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Rebinds this variable to the given `value`.
|
83
|
+
#
|
84
|
+
# @param [Value] value
|
85
|
+
# @return [Value] The previous value, if any.
|
86
|
+
def bind(value)
|
87
|
+
old_value = self.value
|
88
|
+
self.value = value
|
89
|
+
old_value
|
90
|
+
end
|
91
|
+
|
92
|
+
alias_method :bind!, :bind
|
93
|
+
|
94
|
+
##
|
95
|
+
# Unbinds this variable, discarding any currently bound value.
|
96
|
+
#
|
97
|
+
# @return [Value] The previous value, if any.
|
98
|
+
def unbind
|
99
|
+
old_value = self.value
|
100
|
+
self.value = nil
|
101
|
+
old_value
|
102
|
+
end
|
103
|
+
|
104
|
+
alias_method :unbind!, :unbind
|
105
|
+
|
106
|
+
##
|
107
|
+
# Returns this variable as `Hash`.
|
108
|
+
#
|
109
|
+
# @return [Hash{Symbol => Variable}]
|
110
|
+
def variables
|
111
|
+
{ name => self }
|
112
|
+
end
|
113
|
+
|
114
|
+
alias_method :to_hash, :variables
|
115
|
+
|
116
|
+
##
|
117
|
+
# Returns this variable's bindings (if any) as a `Hash`.
|
118
|
+
#
|
119
|
+
# @return [Hash{Symbol => Value}]
|
120
|
+
def bindings
|
121
|
+
unbound? ? {} : { name => value }
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Compares this variable with the given value.
|
126
|
+
#
|
127
|
+
# @param [Value] other
|
128
|
+
# @return [Boolean]
|
129
|
+
def ===(other)
|
130
|
+
if unbound?
|
131
|
+
true # match anything when unbound
|
132
|
+
else
|
133
|
+
value === other
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# Returns a string representation of this variable.
|
139
|
+
#
|
140
|
+
# @return [String]
|
141
|
+
def to_s
|
142
|
+
unbound? ? "?#{name}" : "?#{name}=#{value}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end end
|
data/lib/rdf/reader.rb
CHANGED
@@ -4,15 +4,10 @@ module RDF
|
|
4
4
|
#
|
5
5
|
# @abstract
|
6
6
|
class Reader
|
7
|
-
autoload :NTriples, 'rdf/reader/ntriples'
|
7
|
+
autoload :NTriples, 'rdf/reader/ntriples' # @deprecated
|
8
8
|
|
9
9
|
include Enumerable
|
10
10
|
|
11
|
-
@@subclasses = []
|
12
|
-
@@file_extensions = {}
|
13
|
-
@@content_types = {}
|
14
|
-
@@content_encoding = {}
|
15
|
-
|
16
11
|
##
|
17
12
|
# Enumerates known RDF reader classes.
|
18
13
|
#
|
@@ -22,24 +17,13 @@ module RDF
|
|
22
17
|
!block_given? ? @@subclasses : @@subclasses.each { |klass| yield klass }
|
23
18
|
end
|
24
19
|
|
25
|
-
##
|
26
|
-
# @return [Hash{String => Symbol}]
|
27
|
-
def self.content_types
|
28
|
-
@@content_types
|
29
|
-
end
|
30
|
-
|
31
|
-
##
|
32
|
-
# @return [Hash{Symbol => String}]
|
33
|
-
def self.file_extensions
|
34
|
-
@@file_extensions
|
35
|
-
end
|
36
|
-
|
37
20
|
##
|
38
21
|
# @param [Symbol] format
|
39
22
|
# @return [Class]
|
40
23
|
def self.for(format)
|
41
24
|
klass = case format.to_s.downcase.to_sym
|
42
|
-
when :ntriples then RDF::Reader
|
25
|
+
when :ntriples then RDF::NTriples::Reader
|
26
|
+
else nil # FIXME
|
43
27
|
end
|
44
28
|
end
|
45
29
|
|
@@ -127,23 +111,15 @@ module RDF
|
|
127
111
|
|
128
112
|
private
|
129
113
|
|
114
|
+
@@subclasses = [] # @private
|
115
|
+
|
130
116
|
def self.inherited(child) #:nodoc:
|
131
117
|
@@subclasses << child
|
132
118
|
super
|
133
119
|
end
|
134
120
|
|
135
|
-
def self.
|
136
|
-
|
137
|
-
@@content_types[type] << self
|
138
|
-
|
139
|
-
if options[:extension]
|
140
|
-
extensions = [options[:extension]].flatten.map { |ext| ext.to_sym }
|
141
|
-
extensions.each { |ext| @@file_extensions[ext] = type }
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
def self.content_encoding(encoding)
|
146
|
-
@@content_encoding[self] = encoding.to_sym
|
121
|
+
def self.format(klass)
|
122
|
+
# TODO
|
147
123
|
end
|
148
124
|
|
149
125
|
def lineno
|
data/lib/rdf/reader/ntriples.rb
CHANGED
@@ -1,86 +1,5 @@
|
|
1
|
-
module RDF
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
# @see http://www.w3.org/TR/rdf-testcases/#ntriples
|
6
|
-
class NTriples < Reader
|
7
|
-
content_type 'text/plain', :extension => :nt
|
8
|
-
content_encoding 'ascii'
|
9
|
-
|
10
|
-
protected
|
11
|
-
|
12
|
-
##
|
13
|
-
# @return [Array, nil]
|
14
|
-
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar
|
15
|
-
def read_triple
|
16
|
-
loop do
|
17
|
-
readline.strip! # EOFError thrown on end of input
|
18
|
-
|
19
|
-
unless blank? || read_comment
|
20
|
-
subject = read_uriref || read_bnode || fail_subject
|
21
|
-
predicate = read_uriref || fail_predicate
|
22
|
-
object = read_uriref || read_bnode || read_literal || fail_object
|
23
|
-
return [subject, predicate, object]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
##
|
29
|
-
# @return [Boolean]
|
30
|
-
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (comment)
|
31
|
-
def read_comment
|
32
|
-
match(/^#\s*(.*)$/)
|
33
|
-
end
|
34
|
-
|
35
|
-
##
|
36
|
-
# @return [URI, nil]
|
37
|
-
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (uriref)
|
38
|
-
def read_uriref
|
39
|
-
if uri = match(/^<([^>]+)>/)
|
40
|
-
RDF::URI.parse(uri)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
##
|
45
|
-
# @return [Node, nil]
|
46
|
-
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (nodeID)
|
47
|
-
def read_bnode
|
48
|
-
if node_id = match(/^_:([A-Za-z][A-Za-z0-9]*)/)
|
49
|
-
@nodes[node_id] ||= Object.new # TODO: RDF::Node.new
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
##
|
54
|
-
# @return [String, Literal, nil]
|
55
|
-
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (literal)
|
56
|
-
def read_literal
|
57
|
-
if literal = match(/^"((?:\\"|[^"])*)"/)
|
58
|
-
literal = unescaped(literal)
|
59
|
-
|
60
|
-
if language = match(/^@([a-z]+[\-a-z0-9]*)/)
|
61
|
-
# TODO: RDF::Literal.new(literal, :language => language)
|
62
|
-
literal
|
63
|
-
elsif datatype = match(/^(\^\^)/)
|
64
|
-
# TODO: RDF::Literal.new(literal, :type => read_uriref || fail_object)
|
65
|
-
literal
|
66
|
-
else
|
67
|
-
literal # plain string literal
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
##
|
73
|
-
# @param [String] string
|
74
|
-
# @return [String]
|
75
|
-
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
76
|
-
def unescaped(string)
|
77
|
-
["\t", "\n", "\r", "\"", "\\"].each do |escape|
|
78
|
-
string.gsub!(escape.inspect[1...-1], escape)
|
79
|
-
end
|
80
|
-
string.gsub!(/\\u([0-9A-Fa-f]{4,4})/u) { [$1.hex].pack('U*') }
|
81
|
-
string.gsub!(/\\U([0-9A-Fa-f]{8,8})/u) { [$1.hex].pack('U*') }
|
82
|
-
string
|
83
|
-
end
|
84
|
-
|
1
|
+
module RDF
|
2
|
+
class Reader
|
3
|
+
NTriples = RDF::NTriples::Reader # @deprecated
|
85
4
|
end
|
86
|
-
end
|
5
|
+
end
|
data/lib/rdf/repository.rb
CHANGED
@@ -150,13 +150,15 @@ module RDF
|
|
150
150
|
##
|
151
151
|
# Queries the repository for RDF statements matching the given pattern.
|
152
152
|
#
|
153
|
-
# @param [Statement, Array(Value)] pattern
|
153
|
+
# @param [Query, Statement, Array(Value)] pattern
|
154
154
|
# @yield [statement]
|
155
155
|
# @yieldparam [Statement]
|
156
156
|
# @return [Array<Statement>, nil]
|
157
157
|
def query(pattern, &block)
|
158
158
|
raise TypeError.new("repository is not readable") unless readable?
|
159
159
|
case pattern
|
160
|
+
when Query
|
161
|
+
pattern.execute(self, &block)
|
160
162
|
when Array
|
161
163
|
query(Statement.new(*pattern), &block)
|
162
164
|
when Statement
|