rdf-resource 0.0.1

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.
@@ -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