oa-graph 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.
- checksums.yaml +7 -0
- data/README.md +31 -0
- data/Rakefile +44 -0
- data/lib/oa/graph.rb +191 -0
- data/lib/oa/graph/version.rb +5 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1ee82d3a04f3b61c10ce562151dcdb0e72c90547
|
4
|
+
data.tar.gz: 70110ef9417988e88c8f3c301731767e218e4ced
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7fb5b5f0cdab266514db38680df4016a46dcf9a35338f69a1db00a541ac2c43ad44106c33a6bd698076c68fc5e9baf5548170ea2ffe49b313e4ce2323db73965
|
7
|
+
data.tar.gz: 31470a60d945d2e04501d40b78c10163d6b0f615b0b70c8b202f72a7f25da533b57e1655c57bfbb32dcbac12114c9a2f7d5311b0ddc78a8c71f61f61c6a755d5
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Oa::Graph
|
2
|
+
|
3
|
+
A wrapper class for RDF::Graph that adds methods specific to OpenAnnotation graphs (See http://www.openannotation.org/spec/core/). Intended to be used for a single instance of an OpenAnnotation; adds methods specific to OpenAnnotation RDF::Graph objects.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'oa-graph'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install oa-graph
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/oa-graph/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require "bundler/gem_tasks"
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'rspec/core/rake_task'
|
11
|
+
RSpec::Core::RakeTask.new(:spec)
|
12
|
+
rescue LoadError
|
13
|
+
end
|
14
|
+
|
15
|
+
task :default => :spec
|
16
|
+
|
17
|
+
desc "Generate RDoc with YARD"
|
18
|
+
task :doc => ['doc:generate']
|
19
|
+
|
20
|
+
namespace :doc do
|
21
|
+
begin
|
22
|
+
require 'yard'
|
23
|
+
require 'yard/rake/yardoc_task'
|
24
|
+
|
25
|
+
YARD::Rake::YardocTask.new(:generate) do |yt|
|
26
|
+
yt.files = Dir.glob(File.join('lib', '*.rb')) +
|
27
|
+
Dir.glob(File.join('lib', '**', '*.rb'))
|
28
|
+
|
29
|
+
yt.options = ['--output-dir', 'rdoc', '--readme', 'README.md',
|
30
|
+
'--files', 'LICENSE', '--protected', '--private', '--title',
|
31
|
+
'OA::Graph', '--exclude', 'version.rb']
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
desc "Generate RDoc with YARD"
|
35
|
+
task :generate do
|
36
|
+
abort "Please install the YARD gem to generate rdoc."
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Remove generated documenation"
|
41
|
+
task :clean do
|
42
|
+
rm_r 'rdoc' if File.exists?('rdoc')
|
43
|
+
end
|
44
|
+
end
|
data/lib/oa/graph.rb
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'oa/graph/version'
|
2
|
+
require 'linkeddata'
|
3
|
+
|
4
|
+
module OA
|
5
|
+
# a wrapper class for RDF::Graph that adds methods specific to OpenAnnotation
|
6
|
+
# (http://www.openannotation.org/spec/core/) Annotation objects. This is
|
7
|
+
# intended to be used for an RDF::Graph of a single annotation
|
8
|
+
class Graph
|
9
|
+
|
10
|
+
OA_CONTEXT_URL = 'http://www.w3.org/ns/oa.jsonld'
|
11
|
+
OA_DATED_CONTEXT_URL = 'http://www.w3.org/ns/oa-context-20130208.json'
|
12
|
+
IIIF_CONTEXT_URL = 'http://iiif.io/api/presentation/2/context.json'
|
13
|
+
|
14
|
+
# Class Methods ----------------------------------------------------------------
|
15
|
+
|
16
|
+
# given an RDF::Resource (an RDF::Node or RDF::URI), look for all the
|
17
|
+
# statements with that object as the subject, and recurse through the graph
|
18
|
+
# to find all descendant statements pertaining to the subject
|
19
|
+
# @param subject the RDF object to be used as the subject in the graph
|
20
|
+
# query. Should be an RDF::Node or RDF::URI
|
21
|
+
# @param [RDF::Graph] graph
|
22
|
+
# @return [Array[RDF::Statement]] all the triples with the given subject
|
23
|
+
def self.subject_statements(subject, graph)
|
24
|
+
result = []
|
25
|
+
graph.query([subject, nil, nil]).each { |stmt|
|
26
|
+
result << stmt
|
27
|
+
subject_statements(stmt.object, graph).each { |s| result << s }
|
28
|
+
}
|
29
|
+
result.uniq
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [RDF::Query] query for a subject :s with type of
|
33
|
+
# RDF::Vocab::OA.Annotation
|
34
|
+
def self.anno_query
|
35
|
+
q = RDF::Query.new
|
36
|
+
q << [:s, RDF.type, RDF::Vocab::OA.Annotation]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Instance Methods -------------------------------------------------------------
|
40
|
+
|
41
|
+
# instantiate this class for an RDF::Graph of a single annotation
|
42
|
+
def initialize(rdf_graph)
|
43
|
+
@graph = rdf_graph
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return json-ld representation of graph with OpenAnnotation context as a
|
47
|
+
# url
|
48
|
+
def jsonld_oa
|
49
|
+
inline_context = @graph.dump(:jsonld, context: OA_DATED_CONTEXT_URL)
|
50
|
+
hash_from_json = JSON.parse(inline_context)
|
51
|
+
hash_from_json['@context'] = OA_DATED_CONTEXT_URL
|
52
|
+
hash_from_json.to_json
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return json-ld representation of graph with IIIF context as a url
|
56
|
+
def jsonld_iiif
|
57
|
+
inline_context = @graph.dump(:jsonld, context: IIIF_CONTEXT_URL)
|
58
|
+
hash_from_json = JSON.parse(inline_context)
|
59
|
+
hash_from_json['@context'] = IIIF_CONTEXT_URL
|
60
|
+
hash_from_json.to_json
|
61
|
+
end
|
62
|
+
|
63
|
+
# Canned Query methods -----------------------------------------------------
|
64
|
+
|
65
|
+
# @return [String] the id of this annotation as a url string, or nil if it
|
66
|
+
# is a Node
|
67
|
+
def id_as_url
|
68
|
+
solution = @graph.query self.class.anno_query
|
69
|
+
return if solution && solution.size == 1
|
70
|
+
|
71
|
+
rdf_resource = solution.first.s
|
72
|
+
rdf_resource.to_s if rdf_resource.is_a?(RDF::URI)
|
73
|
+
# TODO: raise exception if not a URI?
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Array<String>] Array of urls expressing the OA motivated_by
|
77
|
+
# values
|
78
|
+
def motivated_by
|
79
|
+
motivations = []
|
80
|
+
q = self.class.anno_query.dup
|
81
|
+
q << [:s, RDF::Vocab::OA.motivatedBy, :motivated_by]
|
82
|
+
solution = @graph.query q
|
83
|
+
if solution && solution.size > 0
|
84
|
+
solution.each { |res|
|
85
|
+
motivations << res.motivated_by.to_s
|
86
|
+
}
|
87
|
+
end
|
88
|
+
# TODO: raise exception if none? (validation)
|
89
|
+
motivations
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param [RDF::URI] predicate either RDF::Vocab::OA.hasTarget or RDF::Vocab::OA.hasBody
|
93
|
+
# @return [Array<String>] urls for the predicate, as an Array of Strings
|
94
|
+
def predicate_urls(predicate)
|
95
|
+
urls = []
|
96
|
+
predicate_solns = @graph.query [nil, predicate, nil]
|
97
|
+
predicate_solns.each { |predicate_stmt|
|
98
|
+
predicate_obj = predicate_stmt.object
|
99
|
+
urls << predicate_obj.to_str.strip if predicate_obj.is_a?(RDF::URI)
|
100
|
+
}
|
101
|
+
urls
|
102
|
+
end
|
103
|
+
|
104
|
+
# For all bodies that are of type ContentAsText, get the characters as a
|
105
|
+
# single String in the returned Array.
|
106
|
+
# @return [Array<String>] body chars as Strings, in an Array (one element
|
107
|
+
# for each contentAsText body)
|
108
|
+
def body_chars
|
109
|
+
result = []
|
110
|
+
q = RDF::Query.new
|
111
|
+
q << [nil, RDF::Vocab::OA.hasBody, :body]
|
112
|
+
q << [:body, RDF.type, RDF::Vocab::CNT.ContentAsText]
|
113
|
+
q << [:body, RDF::Vocab::CNT.chars, :body_chars]
|
114
|
+
solns = @graph.query q
|
115
|
+
solns.each { |soln|
|
116
|
+
result << soln.body_chars.value
|
117
|
+
}
|
118
|
+
result
|
119
|
+
end
|
120
|
+
|
121
|
+
# @return [String] The datetime from the annotatedAt property, or nil
|
122
|
+
def annotated_at
|
123
|
+
solution = @graph.query [nil, RDF::Vocab::OA.annotatedAt, nil]
|
124
|
+
return if solution && solution.size == 1
|
125
|
+
|
126
|
+
solution.first.object.to_s
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
# Changing the Graph -------------------------------------------------------
|
131
|
+
|
132
|
+
# remove all RDF::Vocab::OA.hasBody and .hasTarget statements
|
133
|
+
# and any other statements associated with body and target objects,
|
134
|
+
# leaving all statements to be stored as part of base object in LDP store
|
135
|
+
def remove_non_base_statements
|
136
|
+
remove_has_target_statements
|
137
|
+
remove_has_body_statements
|
138
|
+
end
|
139
|
+
|
140
|
+
# remove all RDF::Vocab::OA.hasBody statements and any other statements associated
|
141
|
+
# with body objects
|
142
|
+
def remove_has_body_statements
|
143
|
+
remove_predicate_and_its_object_statements RDF::Vocab::OA.hasBody
|
144
|
+
end
|
145
|
+
|
146
|
+
# remove all RDF::Vocab::OA.hasTarget statements and any other statements
|
147
|
+
# associated with body objects
|
148
|
+
def remove_has_target_statements
|
149
|
+
remove_predicate_and_its_object_statements RDF::Vocab::OA.hasTarget
|
150
|
+
end
|
151
|
+
|
152
|
+
# remove all such predicate statements and any other statements associated
|
153
|
+
# with predicates' objects
|
154
|
+
def remove_predicate_and_its_object_statements(predicate)
|
155
|
+
predicate_stmts = @graph.query([nil, predicate, nil])
|
156
|
+
predicate_stmts.each { |pstmt|
|
157
|
+
pred_obj = pstmt.object
|
158
|
+
OA::Graph.subject_statements(pred_obj, @graph).each { |s|
|
159
|
+
@graph.delete s
|
160
|
+
} unless !OA::Graph.subject_statements(pred_obj, @graph)
|
161
|
+
@graph.delete pstmt
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
# transform an outer blank node into a null relative URI
|
166
|
+
def make_null_relative_uri_out_of_blank_node
|
167
|
+
anno_stmts = @graph.query([nil, RDF.type, RDF::Vocab::OA.Annotation])
|
168
|
+
anno_rdf_obj = anno_stmts.first.subject
|
169
|
+
if anno_rdf_obj.is_a?(RDF::Node)
|
170
|
+
# use null relative URI representation of blank node
|
171
|
+
anno_subject = RDF::URI.new
|
172
|
+
else # it's already a URI
|
173
|
+
anno_subject = anno_rdf_obj
|
174
|
+
end
|
175
|
+
OA::Graph.subject_statements(anno_rdf_obj, @graph).each { |s|
|
176
|
+
if s.subject == anno_rdf_obj && anno_subject != anno_rdf_obj
|
177
|
+
@graph << RDF::Statement(subject: anno_subject,
|
178
|
+
predicate: s.predicate,
|
179
|
+
object: s.object)
|
180
|
+
@graph.delete s
|
181
|
+
end
|
182
|
+
}
|
183
|
+
end
|
184
|
+
|
185
|
+
# send unknown methods to RDF::Graph
|
186
|
+
def method_missing(sym, *args, &block)
|
187
|
+
@graph.send sym, *args, &block
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oa-graph
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Naomi Dushay
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: linkeddata
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: coveralls
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- ndushay@stanford.edu
|
114
|
+
- darren.weber@stanford.edu
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- README.md
|
120
|
+
- Rakefile
|
121
|
+
- lib/oa/graph.rb
|
122
|
+
- lib/oa/graph/version.rb
|
123
|
+
homepage: https://github.com/sul-dlss/oa-graph
|
124
|
+
licenses:
|
125
|
+
- Apache-2.0
|
126
|
+
metadata: {}
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
requirements: []
|
142
|
+
rubyforge_project:
|
143
|
+
rubygems_version: 2.4.3
|
144
|
+
signing_key:
|
145
|
+
specification_version: 4
|
146
|
+
summary: Wrapper class for RDF::Graph that adds methods specific to OpenAnnotation
|
147
|
+
graphs. http://www.openannotation.org/spec/core/
|
148
|
+
test_files: []
|
149
|
+
has_rdoc:
|