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.
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ module OA
2
+ class Graph
3
+ VERSION = '0.0.1'
4
+ end
5
+ 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: