-
Notifications
You must be signed in to change notification settings - Fork 11
/
context.rb
204 lines (181 loc) · 6.96 KB
/
context.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
module RDF::RDFa
##
# Context representation existing of a hash of terms, prefixes, a default vocabulary and a URI.
#
# Contexts are used for storing RDFa context representations. A representation is created
# by serializing a context graph (typically also in RDFa, but may be in other representations).
#
# The class may be backed by an RDF::Repository, which will be used to retrieve a context graph
# or to load into, if no such graph exists
class Context
include RDF::Util::Logger
# Prefix mappings defined in this context
# @!attribute [r] prefixes
# @return [Hash{Symbol => RDF::URI}]
attr_reader :prefixes
# Term mappings defined in this context
# @!attribute [r] terms
# @return [Hash{Symbol => RDF::URI}]
attr_reader :terms
# Default URI defined for this vocabulary
# @!attribute [r] vocabulary
# @return [RDF::URI]
attr_reader :vocabulary
# URI defining this context
# @!attribute [r] uri
# @return [RDF::URI]
attr_reader :uri
##
# Initialize a new context from the given URI.
#
# Parses the context and places it in the repository and cache
#
# @param [RDF::URI, #to_s] uri URI of context to be represented
# @yield [context]
# @yieldparam [RDF::RDFa::Context] context
# @yieldreturn [void] ignored
# @return [RDF::RDFa::Context]
def initialize(uri, **options, &block)
@uri = RDF::URI.intern(uri)
@prefixes = options.fetch(:prefixes, {})
@terms = options.fetch(:terms, {})
@vocabulary = options[:vocabulary]
@options = options.dup
yield(self) if block_given?
self
end
##
# @return [RDF::Util::Cache]
# @private
def self.cache
require 'rdf/util/cache' unless defined?(::RDF::Util::Cache)
@cache ||= begin
RDF::Util::Cache.new(-1)
end
end
##
# Repository used for saving contexts
# @return [RDF::Repository]
def self.repository
@repository ||= RDF::Repository.new(title: "RDFa Contexts")
end
##
# Set repository used for saving contexts
# @param [RDF::Repository] repo
# @return [RDF::Repository]
def self.repository=(repo)
unless repo.supports?(:graph_name)
if respond_to?(:log_fatal)
log_fatal("Context Repository must support graph_name", exception: ContextError)
else
abort("Context Repository must support graph_name")
end
end
@repository = repo
end
# Return a context faulting through the cache
# @return [RDF::RDFa::Context]
def self.find(uri)
uri = RDF::URI.intern(uri)
return cache[uri] unless cache[uri].nil?
# Two part creation to prevent re-entrancy problems if p1 => p2 and p2 => p1
# Return something to make the caller happy if we're re-entered
cache[uri] = Struct.new(:prefixes, :terms, :vocabulary).new({}, {}, nil)
# Now do the actual load
cache[uri] = new(uri) do |context|
log_debug("process_context: retrieve context <#{uri}>") if respond_to?(:log_debug)
Context.load(uri)
context.parse(repository.query({graph_name: uri}))
end
rescue Exception => e
if respond_to?(:log_fatal)
log_error("Context #{uri}: #{e.message}")
else
abort("Context #{uri}: #{e.message}")
end
end
# Load context into repository
def self.load(uri)
uri = RDF::URI.intern(uri)
repository.load(uri.to_s, base_uri: uri, graph_name: uri) unless repository.has_graph?(uri)
end
# @return [RDF::Repository]
def repository
Context.repository
end
##
# Defines the given named URI prefix for this context.
#
# @example Defining a URI prefix
# context.prefix :dc, RDF::URI('http://purl.org/dc/terms/')
#
# @example Returning a URI prefix
# context.prefix(:dc) #=> RDF::URI('http://purl.org/dc/terms/')
#
# @param [Symbol, #to_s] name
# @param [RDF::URI, #to_s] uri
# @return [RDF::URI]
def prefix(name, uri = nil)
name = name.to_s.empty? ? nil : (name.respond_to?(:to_sym) ? name.to_sym : name.to_s.to_sym)
uri.nil? ? prefixes[name] : prefixes[name] = uri
end
##
# Defines the given named URI term for this context.
#
# @example Defining a URI term
# context.term :title, RDF::URI('http://purl.org/dc/terms/title')
#
# @example Returning a URI context
# context.term(:title) #=> RDF::URI('http://purl.org/dc/terms/TITLE')
#
# @param [Symbol, #to_s] name
# @param [RDF::URI, #to_s] uri
# @return [RDF::URI]
def term(name, uri = nil)
name = name.to_s.empty? ? nil : (name.respond_to?(:to_sym) ? name.to_sym : name.to_s.to_sym)
uri.nil? ? terms[name] : terms[name] = uri
end
##
# Extract vocabulary, prefix mappings and terms from a enumerable object into an instance
#
# @param [RDF::Enumerable, Enumerator] enumerable
# @return [void] ignored
def parse(enumerable)
log_debug("process_context: parse context <#{uri}>") if respond_to?(:log_debug)
resource_info = {}
enumerable.each do |statement|
res = resource_info[statement.subject] ||= {}
next unless statement.object.is_a?(RDF::Literal)
log_debug("process_context: statement=#{statement.inspect}") if respond_to?(:log_debug)
%w(uri term prefix vocabulary).each do |term|
res[term] ||= statement.object.value if statement.predicate == RDF::RDFA[term]
end
end
resource_info.values.each do |res|
# If one of the objects is not a Literal or if there are additional rdfa:uri or rdfa:term
# predicates sharing the same subject, no mapping is created.
uri = res["uri"]
term = res["term"]
prefix = res["prefix"]
vocab = res["vocabulary"]
log_debug("process_context: uri=#{uri.inspect}, term=#{term.inspect}, prefix=#{prefix.inspect}, vocabulary=#{vocab.inspect}") if respond_to?(:log_debug)
@vocabulary = vocab if vocab
# For every extracted triple that is the common subject of an rdfa:prefix and an rdfa:uri
# predicate, create a mapping from the object literal of the rdfa:prefix predicate to the
# object literal of the rdfa:uri predicate. Add or update this mapping in the local list of
# URI mappings after transforming the 'prefix' component to lower-case.
# For every extracted
prefix(prefix.downcase, uri) if uri && prefix && prefix != "_"
# triple that is the common subject of an rdfa:term and an rdfa:uri predicate, create a
# mapping from the object literal of the rdfa:term predicate to the object literal of the
# rdfa:uri predicate. Add or update this mapping in the local term mappings.
term(term, uri) if term && uri
end
end
end
##
# The base class for RDF context errors.
class ContextError < IOError; end
end
# Load cooked contexts
Dir.glob(File.join(File.expand_path(File.dirname(__FILE__)), 'context', '*')).each {|f| load f}