rdf-resource 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ [![Build Status](https://travis-ci.org/sul-dlss/rdf-resource.svg?branch=master)](https://travis-ci.org/sul-dlss/rdf-resource) [![Coverage Status](https://coveralls.io/repos/sul-dlss/rdf-resource/badge.png)](https://coveralls.io/r/sul-dlss/rdf-resource) [![Dependency Status](https://gemnasium.com/sul-dlss/rdf-resource.svg)](https://gemnasium.com/sul-dlss/rdf-resource) [![Gem Version](https://badge.fury.io/rb/rdf-resource.svg)](http://badge.fury.io/rb/rdf-resource)
2
+
3
+ # rdf-resource
4
+ A utility for working with RDF resources and vocabularies
5
+
6
+ It uses RDF.rb and several RDF::Vocab, with options for
7
+ caching RDF resources.
8
+
9
+ See https://github.com/sul-dlss/rdf-resource/blob/master/lib/rdf-resource/resource.rb
@@ -0,0 +1,45 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ RSpec::Core::RakeTask.new(:spec)
4
+
5
+ require 'rdf'
6
+ require 'linkeddata'
7
+ require 'rdf/cli/vocab-loader'
8
+
9
+ desc 'Default: run specs.'
10
+ task :default => :spec
11
+ task :specs => :spec
12
+
13
+ # Derived from https://raw.githubusercontent.com/ruby-rdf/rdf/develop/Rakefile
14
+ desc "Generate Vocabularies"
15
+ vocab_sources = {
16
+ Content: {
17
+ uri: 'http://www.w3.org/2011/content#',
18
+ source: 'http://www.w3.org/2011/content',
19
+ strict: true
20
+ },
21
+ }
22
+
23
+ task :gen_vocabs => vocab_sources.keys.map {|v| "lib/rdf/vocab/#{v}.rb"}
24
+
25
+ vocab_sources.each do |id, v|
26
+ file "lib/rdf/vocab/#{id}.rb" => :do_build do
27
+ puts "Generate lib/rdf/vocab/#{id}.rb"
28
+ begin
29
+ out = StringIO.new
30
+ loader = RDF::VocabularyLoader.new(id.to_s.upcase)
31
+ loader.uri = v[:uri]
32
+ loader.source = v[:source] if v[:source]
33
+ loader.extra = v[:extra] if v[:extra]
34
+ loader.strict = v.fetch(:strict, true)
35
+ loader.output = out
36
+ loader.run
37
+ out.rewind
38
+ File.open("lib/rdf/vocab/#{id}.rb", "w") {|f| f.write out.read}
39
+ rescue
40
+ puts "Failed to load #{id}: #{$!.message}"
41
+ end
42
+ end
43
+ end
44
+
45
+ task :do_build
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rdf-resource'
3
+ CONFIG = RDFResource.configuration
4
+ binding.pry
5
+
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ system "find . -name '*.rb' | ctags -f .tags --languages=Ruby -L -"
3
+
4
+ if File.exist? './Gemfile'
5
+ require 'bundler'
6
+ paths = Bundler.load.specs.map(&:full_gem_path).join(' ')
7
+ system "ctags -R -f .gemtags --languages=Ruby #{paths}"
8
+ end
@@ -0,0 +1,27 @@
1
+ require 'requires'
2
+
3
+ module RDFResource
4
+
5
+ AGENT = RDF::URI.parse('https://github.com/sul-dlss/rdf-resource')
6
+
7
+ # configuration at the module level, see
8
+ # http://brandonhilkert.com/blog/ruby-gem-configuration-patterns/
9
+
10
+ class << self
11
+ attr_writer :configuration
12
+ end
13
+
14
+ def self.configuration
15
+ @configuration ||= Configuration.new
16
+ end
17
+
18
+ def self.reset
19
+ @configuration = Configuration.new
20
+ end
21
+
22
+ def self.configure
23
+ yield(configuration)
24
+ end
25
+
26
+ end
27
+
@@ -0,0 +1,64 @@
1
+
2
+ module RDFResource
3
+
4
+ class Configuration
5
+
6
+ attr_accessor :debug
7
+
8
+ attr_accessor :prefixes
9
+
10
+ attr_accessor :use_foaf
11
+ attr_accessor :use_schema
12
+
13
+ attr_accessor :log_file
14
+ attr_accessor :logger
15
+
16
+ def initialize
17
+ @debug = env_boolean('DEBUG')
18
+
19
+ # Vocabulary options
20
+ # foaf:Person or schema:Person or both?
21
+ @use_foaf = env_boolean('USE_FOAF')
22
+ @use_schema = env_boolean('USE_SCHEMA') # schema.org
23
+
24
+ # RDF prefixes
25
+ @prefixes = {}
26
+ @prefixes['bf'] = 'http://bibframe.org/vocab/'
27
+ @prefixes['foaf'] = 'http://xmlns.com/foaf/0.1/'
28
+ @prefixes['isni'] = 'http://www.isni.org/isni/'
29
+ @prefixes['loc_names'] = 'http://id.loc.gov/authorities/names/'
30
+ @prefixes['loc_subjects'] = 'http://id.loc.gov/authorities/subjects/'
31
+ @prefixes['rdf'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
32
+ @prefixes['rdfs'] = 'http://www.w3.org/2000/01/rdf-schema#'
33
+ @prefixes['schema'] = 'http://schema.org/'
34
+ @prefixes['owl'] = 'http://www.w3.org/2002/07/owl#'
35
+ @prefixes['viaf'] = 'http://viaf.org/viaf/'
36
+
37
+ # logging
38
+ log_file = ENV['RDF_LOG_FILE'] || 'log/rdf-resource.log'
39
+ log_file = File.absolute_path log_file
40
+ @log_file = log_file
41
+ log_path = File.dirname log_file
42
+ unless File.directory? log_path
43
+ # try to create the log directory
44
+ Dir.mkdir log_path rescue nil
45
+ end
46
+ begin
47
+ log_file = File.new(@log_file, 'w+')
48
+ rescue
49
+ log_file = $stderr
50
+ @log_file = 'STDERR'
51
+ end
52
+ @logger = Logger.new(log_file, 'monthly')
53
+ @logger.level = @debug ? Logger::DEBUG : Logger::INFO
54
+
55
+ end
56
+
57
+ def env_boolean(var)
58
+ # check if an ENV variable is true, use false as default
59
+ ENV[var].to_s.upcase == 'TRUE' rescue false
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,265 @@
1
+
2
+ module RDFResource
3
+
4
+ class Resource
5
+
6
+ @@config = nil
7
+
8
+ def self.http_head_request(url)
9
+ uri = nil
10
+ begin
11
+ response = RestClient.head(url)
12
+ uri = response.args[:url]
13
+ rescue
14
+ @@config.logger.error "RestClient.head failed for #{url}"
15
+ begin
16
+ response = RestClient.get(url)
17
+ uri = response.args[:url]
18
+ rescue
19
+ @@config.logger.error "RestClient.get failed for #{url}"
20
+ end
21
+ end
22
+ uri
23
+ end
24
+
25
+
26
+ attr_accessor :iri
27
+
28
+ def initialize(uri=nil)
29
+ @@agent ||= RDFResource::AGENT
30
+ @@config ||= RDFResource.configuration
31
+ if uri =~ /\A#{URI::regexp}\z/
32
+ uri = Addressable::URI.parse(uri.to_s) rescue nil
33
+ end
34
+ raise 'invalid uri' unless uri.instance_of? Addressable::URI
35
+ @iri = uri
36
+ end
37
+
38
+ def id
39
+ @iri.basename
40
+ end
41
+
42
+ def iri_type?(type)
43
+ iri_types.include? RDF::URI.parse(type)
44
+ end
45
+
46
+ def iri_types
47
+ q = [rdf_uri, RDF.type, :o]
48
+ rdf.query(q).collect {|s| s.object }
49
+ end
50
+
51
+ # Assert PROV.SoftwareAgent and PROV.generatedAtTime
52
+ def provenance
53
+ s = [rdf_uri, RDF::PROV.SoftwareAgent, @@agent]
54
+ rdf.insert(s)
55
+ s = [rdf_uri, RDF::PROV.generatedAtTime, rdf_now]
56
+ rdf.insert(s)
57
+ end
58
+
59
+ # Retrieve RDF graph from iri; the results may be cached when
60
+ # the RDF::Graph.load method uses RestClient and it is configured
61
+ # to cache results.
62
+ # This method is often overloaded in subclasses because
63
+ # RDF services use variations in the URL 'extension' patterns.
64
+ # @return [RDF::Graph|nil] an RDF graph
65
+ def rdf
66
+ return @rdf unless @rdf.nil?
67
+ uri4rdf = @iri.to_s
68
+ tries = 0
69
+ begin
70
+ tries += 1
71
+ @rdf = RDF::Graph.load(uri4rdf)
72
+ rescue
73
+ sleep 1*tries
74
+ retry if tries < 3
75
+ binding.pry if @@config.debug
76
+ @@config.logger.error("Failed to retrieve RDF for #{uri4rdf}")
77
+ @rdf = nil
78
+ end
79
+ end
80
+
81
+ # RDF query to find all objects of a predicate
82
+ # @param predicate [RDF::URI] An RDF predicate, the ?p in ?s ?p ?o
83
+ # @return [Array] The objects of predicate, the ?o in ?s ?p ?o
84
+ def query_predicate_objects(predicate)
85
+ q = [:s, predicate, :o]
86
+ rdf.query(q).collect {|s| s.object }
87
+ end
88
+
89
+ # RDF query to find all subjects with a predicate
90
+ # @param predicate [RDF::URI] An RDF predicate, the ?p in ?s ?p ?o
91
+ # @return [Array] The subjects with predicate, the ?s in ?s ?p ?o
92
+ def query_predicate_subjects(predicate)
93
+ q = [:s, predicate, :o]
94
+ rdf.query(q).collect {|s| s.subject }
95
+ end
96
+
97
+ # Regexp search to find an object matching a string, if it belongs to @iri
98
+ # @param id [String] A string literal used to construct a Regexp
99
+ # @return [RDF::URI] The first object matching the Regexp
100
+ def rdf_find_object(id)
101
+ return nil unless rdf_valid?
102
+ rdf.each_statement do |s|
103
+ if s.subject == @iri.to_s
104
+ return s.object if s.object.to_s =~ Regexp.new(id, Regexp::IGNORECASE)
105
+ end
106
+ end
107
+ nil
108
+ end
109
+
110
+ # Regexp search to find a subject matching a string
111
+ # @param id [String] A string literal used to construct a Regexp
112
+ # @return [RDF::URI] The first subject matching the Regexp
113
+ def rdf_find_subject(id)
114
+ return nil unless rdf_valid?
115
+ rdf.each_subject do |s|
116
+ return s if s.to_s =~ Regexp.new(id, Regexp::IGNORECASE)
117
+ end
118
+ nil
119
+ end
120
+
121
+ # @param object [RDF::Node] An RDF blank node
122
+ # @return [RDF::Graph] graph of recursive resolution for a blank node
123
+ def rdf_expand_blank_nodes(object)
124
+ g = RDF::Graph.new
125
+ if object.node?
126
+ rdf.query([object, nil, nil]) do |s,p,o|
127
+ g << [s,p,o]
128
+ g << rdf_expand_blank_nodes(o) if o.node?
129
+ end
130
+ end
131
+ g
132
+ end
133
+
134
+ # ----
135
+ # RDF::Graph convenience wrappers
136
+
137
+ def rdf_insert(uriS, uriP, uriO)
138
+ @rdf.insert RDF::Statement(uriS, uriP, uriO)
139
+ end
140
+ def rdf_insert_sameAs(uriS, uriO)
141
+ rdf_insert(uriS, RDF::OWL.sameAs, uriO)
142
+ end
143
+ def rdf_insert_seeAlso(uriS, uriO)
144
+ rdf_insert(uriS, RDF::RDFS.seeAlso, uriO)
145
+ end
146
+ def rdf_insert_creator(uriS, uriO)
147
+ rdf_insert(uriS, RDF::SCHEMA.creator, uriO)
148
+ end
149
+ def rdf_insert_contributor(uriS, uriO)
150
+ rdf_insert(uriS, RDF::SCHEMA.contributor, uriO)
151
+ end
152
+ def rdf_insert_editor(uriS, uriO)
153
+ rdf_insert(uriS, RDF::SCHEMA.editor, uriO)
154
+ end
155
+ def rdf_insert_exampleOfWork(uriS, uriO)
156
+ rdf_insert(uriS, RDF::SCHEMA.exampleOfWork, uriO)
157
+ end
158
+ def rdf_insert_foafFocus(uriS, uriO)
159
+ # http://xmlns.com/foaf/spec/#term_focus
160
+ # relates SKOS:Concept to a 'real world thing'
161
+ rdf_insert(uriS, RDF::FOAF.focus, uriO)
162
+ end
163
+ def rdf_insert_name(uriS, name)
164
+ rdf_insert(uriS, RDF::FOAF.name, name) if @@config.use_foaf
165
+ rdf_insert(uriS, RDF::SCHEMA.name, name) if @@config.use_schema
166
+ end
167
+
168
+ def rdf_now
169
+ RDF::Literal.new(Time.now.utc, :datatype => RDF::XSD.dateTime)
170
+ end
171
+
172
+ def rdf_uri
173
+ RDF::URI.new(@iri)
174
+ end
175
+
176
+ # Methods that assert RDF.type
177
+
178
+ def rdf_insert_type(uriS, uriO)
179
+ rdf_insert(uriS, RDF.type, uriO)
180
+ end
181
+
182
+ def rdf_type_agent(uriS)
183
+ # Note: schema.org has no immediate parent for Person or Organization
184
+ rdf_insert_type(uriS, RDF::FOAF.Agent) if @@config.use_foaf
185
+ rdf_insert_type(uriS, RDF::SCHEMA.Thing) if @@config.use_schema
186
+ end
187
+
188
+ def rdf_type_concept(uriS)
189
+ rdf_insert_type(uriS, RDF::SKOS.Concept)
190
+ end
191
+
192
+ def rdf_type_organization(uriS)
193
+ rdf_insert_type(uriS, RDF::FOAF.Organization) if @@config.use_foaf
194
+ rdf_insert_type(uriS, RDF::SCHEMA.Organization) if @@config.use_schema
195
+ end
196
+
197
+ def rdf_type_person(uriS)
198
+ rdf_insert_type(uriS, RDF::FOAF.Person) if @@config.use_foaf
199
+ rdf_insert_type(uriS, RDF::SCHEMA.Person) if @@config.use_schema
200
+ end
201
+
202
+ def rdf_valid?
203
+ iri_types.length > 0
204
+ end
205
+
206
+
207
+
208
+ # ---
209
+ # HTTP methods
210
+
211
+ # @param url [String|URI] A URL that can be resolved via HTTP request
212
+ # @return [String|nil] the URL, after resolving redirections
213
+ def resolve_url(url)
214
+ begin
215
+ # RestClient does all the response code handling and redirection.
216
+ url = Resource.http_head_request(url)
217
+ if url.nil?
218
+ @@config.logger.warn "#{@iri}\t// #{url}"
219
+ else
220
+ @@config.logger.debug "Mapped #{@iri}\t-> #{url}"
221
+ end
222
+ rescue
223
+ binding.pry if @@config.debug
224
+ @@config.logger.error "unknown http error for #{@iri}"
225
+ url = nil
226
+ end
227
+ url
228
+ end
229
+
230
+ def same_as_org_graph
231
+ return @same_as_org_graph unless @same_as_org_graph.nil?
232
+ same_as_url = 'http://sameas.org/rdf?uri=' + URI.encode(@iri.to_s)
233
+ @same_as_org_graph = RDF::Graph.load(same_as_url)
234
+ end
235
+ def same_as_org_query
236
+ # q = SPARQL.parse("SELECT * WHERE { <#{@iri}> <http://www.w3.org/2002/07/owl#sameAs> ?o }")
237
+ q = [rdf_uri, RDF::OWL.sameAs, nil]
238
+ same_as_org_graph.query(q).collect {|s| s.object }
239
+ end
240
+
241
+
242
+
243
+ # ---
244
+ # Transforms or Serialization
245
+
246
+ # A json-ld object for the rdf resource
247
+ def as_jsonld
248
+ JSON::LD::API::fromRdf(rdf)
249
+ end
250
+
251
+ # A json-ld serialization of the rdf resource
252
+ def to_jsonld
253
+ rdf.dump(:jsonld, standard_prefixes: true)
254
+ end
255
+
256
+ # A turtle serialization of the rdf resource
257
+ def to_ttl
258
+ rdf.dump(:ttl, standard_prefixes: true)
259
+ end
260
+
261
+ end
262
+
263
+ end
264
+
265
+
@@ -0,0 +1,112 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # This file generated automatically using vocab-fetch from http://www.w3.org/2011/content
3
+ require 'rdf'
4
+ module RDF
5
+ class CONTENT < RDF::StrictVocabulary("http://www.w3.org/2011/content#")
6
+
7
+ # Class definitions
8
+ term :Content,
9
+ comment: %(The content.).freeze,
10
+ label: "Content".freeze,
11
+ type: ["rdfs:Class".freeze, "owl:Class".freeze]
12
+ term :ContentAsBase64,
13
+ comment: %(The base64 encoded content \(can be used for binary content\).).freeze,
14
+ label: "Base64 content".freeze,
15
+ subClassOf: "http://www.w3.org/2011/content#Content".freeze,
16
+ type: ["rdfs:Class".freeze, "owl:Class".freeze]
17
+ term :ContentAsText,
18
+ comment: %(The text content \(can be used for text content\).).freeze,
19
+ label: "Text content".freeze,
20
+ subClassOf: "http://www.w3.org/2011/content#Content".freeze,
21
+ type: ["rdfs:Class".freeze, "owl:Class".freeze]
22
+ term :ContentAsXML,
23
+ comment: %(The XML content \(can only be used for XML-wellformed content\).).freeze,
24
+ label: "XML content".freeze,
25
+ subClassOf: "http://www.w3.org/2011/content#Content".freeze,
26
+ type: ["rdfs:Class".freeze, "owl:Class".freeze]
27
+ term :DoctypeDecl,
28
+ comment: %(The document type declaration.).freeze,
29
+ label: "Document type declaration".freeze,
30
+ type: ["rdfs:Class".freeze, "owl:Class".freeze]
31
+
32
+ # Property definitions
33
+ property :bytes,
34
+ comment: %(The Base64 encoded byte sequence of the content.).freeze,
35
+ domain: "http://www.w3.org/2011/content#ContentAsBase64".freeze,
36
+ label: "Base64 encoded byte sequence".freeze,
37
+ range: "xsd:base64Binary".freeze,
38
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
39
+ property :characterEncoding,
40
+ comment: %(The character encoding used to create a character sequence from a byte sequence or vice versa.).freeze,
41
+ domain: "http://www.w3.org/2011/content#Content".freeze,
42
+ label: "Character encoding".freeze,
43
+ range: "rdfs:Literal".freeze,
44
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
45
+ property :chars,
46
+ comment: %(The character sequence of the text content.).freeze,
47
+ domain: "http://www.w3.org/2011/content#ContentAsText".freeze,
48
+ label: "Character sequence".freeze,
49
+ range: "rdfs:Literal".freeze,
50
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
51
+ property :declaredEncoding,
52
+ comment: %(The character encoding declared in the XML declaration.).freeze,
53
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
54
+ label: "XML character encoding".freeze,
55
+ range: "rdfs:Literal".freeze,
56
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
57
+ property :doctypeName,
58
+ comment: %(The document type name.).freeze,
59
+ domain: "http://www.w3.org/2011/content#DoctypeDecl".freeze,
60
+ label: "Document type name".freeze,
61
+ range: "rdfs:Literal".freeze,
62
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
63
+ property :dtDecl,
64
+ comment: %(The document type declaration.).freeze,
65
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
66
+ label: "Document type declaration".freeze,
67
+ range: "http://www.w3.org/2011/content#DoctypeDecl".freeze,
68
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
69
+ property :internalSubset,
70
+ comment: %(The internal document type definition subset within the document type declarations.).freeze,
71
+ domain: "http://www.w3.org/2011/content#DoctypeDecl".freeze,
72
+ label: "Internal DTD subset".freeze,
73
+ range: "rdfs:Literal".freeze,
74
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
75
+ property :leadingMisc,
76
+ comment: %(The XML content preceding the document type declaration.).freeze,
77
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
78
+ label: "XML leading misc".freeze,
79
+ range: "rdfs:XMLLiteral".freeze,
80
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
81
+ property :publicId,
82
+ comment: %(The document type declarations's public identifier.).freeze,
83
+ domain: "http://www.w3.org/2011/content#DoctypeDecl".freeze,
84
+ label: "Public ID".freeze,
85
+ range: "rdfs:Literal".freeze,
86
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
87
+ property :rest,
88
+ comment: %(The XML content following the document type declaration.).freeze,
89
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
90
+ label: "XML rest".freeze,
91
+ range: "rdfs:XMLLiteral".freeze,
92
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
93
+ property :standalone,
94
+ comment: %(The standalone declaration in the XML declaration.).freeze,
95
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
96
+ label: "XML standalone document declaration".freeze,
97
+ range: "rdfs:Literal".freeze,
98
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
99
+ property :systemId,
100
+ comment: %(The document type declarations's system identifier \(typed: xsd:anyURI\)).freeze,
101
+ domain: "http://www.w3.org/2011/content#DoctypeDecl".freeze,
102
+ label: "System ID".freeze,
103
+ range: "xsd:anyURI".freeze,
104
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
105
+ property :version,
106
+ comment: %(The XML version declared in the XML declaration.).freeze,
107
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
108
+ label: "XML version".freeze,
109
+ range: "rdfs:Literal".freeze,
110
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
111
+ end
112
+ end