active_annotations 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,224 @@
1
+ require 'open-uri'
2
+ require 'securerandom'
3
+ require 'yaml'
4
+ require 'json/ld'
5
+ require 'rdf/vocab/dc'
6
+ require 'rdf/vocab/oa'
7
+ require 'rdf/vocab/cnt'
8
+ require 'rdf/vocab/dcmitype'
9
+ require 'rdf/vocab/foaf'
10
+
11
+ module ActiveAnnotations
12
+ class RDFAnnotation
13
+ RDF::Vocab::DC = RDF::DC unless defined?(RDF::Vocab::DC)
14
+ RDF::Vocab::FOAF = RDF::FOAF unless defined?(RDF::Vocab::FOAF)
15
+
16
+ attr_reader :graph
17
+ CONTEXT_URI = 'http://www.w3.org/ns/oa.jsonld'
18
+
19
+ def self.from_jsonld(json)
20
+ content = JSON.parse(json)
21
+ self.new(JSON::LD::API.toRDF(content, documentLoader: DocumentLoader.document_loader))
22
+ end
23
+
24
+ def to_jsonld(opts={})
25
+ input = JSON.parse(graph.dump :jsonld, standard_prefixes: true, prefixes: { oa: RDF::Vocab::OA.to_iri.value })
26
+ frame = YAML.load(File.read(File.expand_path('../frame.yml',__FILE__)))
27
+ output = JSON::LD::API.frame(input, frame, omitDefault: true, documentLoader: DocumentLoader.document_loader)
28
+ output.merge!(output.delete('@graph')[0])
29
+ if opts[:pretty_json]
30
+ JSON.pretty_generate output
31
+ else
32
+ output.to_json
33
+ end
34
+ end
35
+
36
+ def initialize(content=nil)
37
+ @graph = RDF::Graph.new
38
+ if content.nil?
39
+ self.add_default_content!
40
+ else
41
+ @graph << content
42
+ end
43
+ end
44
+
45
+ def get_value(s, p)
46
+ return nil if s.nil?
47
+ statement = graph.first(subject: s, predicate: p)
48
+ if statement.nil?
49
+ return nil
50
+ elsif statement.object.is_a?(RDF::Literal)
51
+ statement.object.object
52
+ else
53
+ statement.object.value
54
+ end
55
+ end
56
+
57
+ def set_value(s, p, value)
58
+ return nil if s.nil?
59
+ @graph.delete({ subject: s, predicate: p })
60
+ @graph << RDF::Statement.new(s, p, value) unless value.nil?
61
+ end
62
+
63
+ def add_statements(*statements)
64
+ statements.each { |statement| @graph << statement }
65
+ end
66
+
67
+ def add_default_content!
68
+ aid = new_id
69
+ add_statements(
70
+ RDF::Statement.new(aid, RDF.type, RDF::Vocab::OA.Annotation),
71
+ RDF::Statement.new(aid, RDF::Vocab::OA.annotatedAt, DateTime.now)
72
+ )
73
+ end
74
+
75
+ def ensure_body!
76
+ if body_id.nil?
77
+ bid = new_id
78
+ add_statements(
79
+ RDF::Statement.new(annotation_id, RDF::Vocab::OA.hasBody, bid),
80
+ RDF::Statement.new(bid, RDF.type, RDF::Vocab::DCMIType.Text),
81
+ RDF::Statement.new(bid, RDF.type, RDF::Vocab::CNT.ContentAsText)
82
+ )
83
+ end
84
+ end
85
+
86
+ def ensure_target!
87
+ if target_id.nil?
88
+ tid = new_id
89
+ add_statements(
90
+ RDF::Statement.new(annotation_id, RDF::Vocab::OA.hasTarget, tid),
91
+ RDF::Statement.new(tid, RDF.type, RDF::Vocab::OA.SpecificResource)
92
+ )
93
+ end
94
+ end
95
+
96
+ def ensure_selector!
97
+ if selector_id.nil?
98
+ ensure_target!
99
+ sid = new_id
100
+ add_statements(
101
+ RDF::Statement.new(target_id, RDF::Vocab::OA.hasSelector, sid),
102
+ RDF::Statement.new(sid, RDF.type, RDF::Vocab::OA.FragmentSelector),
103
+ RDF::Statement.new(sid, RDF::Vocab::DC.conformsTo, RDF::URI('http://www.w3.org/TR/media-frags/'))
104
+ )
105
+ end
106
+ end
107
+
108
+ def new_id
109
+ RDF::URI.new("urn:uuid:#{SecureRandom.uuid}")
110
+ end
111
+
112
+ def find_id(type)
113
+ statement = @graph.first(predicate: RDF.type, object: type)
114
+ statement.nil? ? nil : statement.subject
115
+ end
116
+
117
+ def annotation_id
118
+ find_id(RDF::Vocab::OA.Annotation)
119
+ end
120
+
121
+ def body_id
122
+ statement = @graph.first(subject: annotation_id, predicate: RDF::Vocab::OA.hasBody)
123
+ statement.nil? ? nil : statement.object
124
+ end
125
+
126
+ def target_id
127
+ find_id(RDF::Vocab::OA.SpecificResource)
128
+ end
129
+
130
+ def selector_id
131
+ find_id(RDF::Vocab::OA.FragmentSelector)
132
+ end
133
+
134
+ def fragment_value
135
+ graph.first(subject: selector_id, predicate: RDF.value)
136
+ end
137
+
138
+ def fragment_value=(value)
139
+ ensure_selector!
140
+ set_value(selector_id, RDF.value, value)
141
+ end
142
+
143
+ def start_time
144
+ value = fragment_value.nil? ? nil : fragment_value.object.value.scan(/^t=(.*)$/).flatten.first.split(/,/)[0]
145
+ Float(value)
146
+ rescue
147
+ value
148
+ end
149
+
150
+ def start_time=(value)
151
+ self.fragment_value = "t=#{[value, end_time].join(',')}"
152
+ end
153
+
154
+ def end_time
155
+ value = fragment_value.nil? ? nil : fragment_value.object.value.scan(/^t=(.*)$/).flatten.first.split(/,/)[1]
156
+ Float(value)
157
+ rescue
158
+ value
159
+ end
160
+
161
+ def end_time=(value)
162
+ self.fragment_value = "t=#{[start_time, value].join(',')}"
163
+ end
164
+
165
+ def content
166
+ get_value(body_id, RDF::Vocab::CNT.chars)
167
+ end
168
+
169
+ def content=(value)
170
+ ensure_body!
171
+ set_value(body_id, RDF::Vocab::CNT.chars, value)
172
+ end
173
+
174
+ def annotated_by
175
+ get_value(annotation_id, RDF::Vocab::OA.annotatedBy)
176
+ end
177
+
178
+ def annotated_by=(value)
179
+ unless annotated_by.nil?
180
+ @graph.delete({ subject: RDF::URI(annotated_by) })
181
+ end
182
+
183
+ value = value.nil? ? nil : RDF::URI(value)
184
+ set_value(annotation_id, RDF::Vocab::OA.annotatedBy, value)
185
+ set_value(value, RDF.type, RDF::Vocab::FOAF.Person)
186
+ end
187
+
188
+ def annotated_at
189
+ get_value(annotation_id, RDF::Vocab::OA.annotatedAt)
190
+ end
191
+
192
+ def annotated_at=(value)
193
+ set_value(annotation_id, RDF::Vocab::OA.annotatedAt, value)
194
+ end
195
+
196
+ def source
197
+ # TODO: Replace this with some way of retrieving the actual source
198
+ get_value(target_id, RDF::Vocab::OA.hasSource)
199
+ end
200
+
201
+ def source=(value)
202
+ unless target_id.nil?
203
+ statement = @graph.first(subject: target_id, predicate: RDF::Vocab::OA.hasSource)
204
+ @graph.delete({ subject: statement.object, predicate: RDF.type }) unless statement.nil?
205
+ @graph.delete({ subject: target_id, predicate: RDF::Vocab::OA.hasSource })
206
+ end
207
+ unless value.nil?
208
+ ensure_target!
209
+ add_statements(
210
+ RDF::Statement.new(target_id, RDF::Vocab::OA.hasSource, RDF::URI(value.rdf_uri)),
211
+ RDF::Statement.new(RDF::URI(value.rdf_uri), RDF.type, value.rdf_type)
212
+ )
213
+ end
214
+ end
215
+
216
+ def label
217
+ get_value(annotation_id, RDF::RDFS.label)
218
+ end
219
+
220
+ def label=(value)
221
+ set_value(annotation_id, RDF::RDFS.label, value)
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveAnnotations
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "active_annotations/version"
2
+ require "active_annotations/rdf_annotation"
3
+ require "active_annotations/document_loader"
4
+ require "active_annotations/engine"
5
+
6
+ module ActiveAnnotations
7
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveAnnotations
2
+ class InstallGenerator < Rails::Generators::Base
3
+ desc "This generator copies the ActiveAnnotations database migrations to /db/migrate"
4
+
5
+ def copy_migrations
6
+ rake "active_annotations:install:migrations"
7
+ rake "db:migrate"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ require 'engine_cart/rake_task'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.pattern = 'spec/**/*_spec.rb'
6
+ end
7
+
8
+ EngineCart.fingerprint_proc = EngineCart.rails_fingerprint_proc
9
+
10
+ desc "Run test suite"
11
+ task :ci => ['engine_cart:generate'] do
12
+ Rake::Task['spec'].invoke
13
+ end
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_annotations
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael B. Klein
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-09-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json-ld
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: rdf-vocab
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
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: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.11'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: engine_cart
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: faker
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
+ - !ruby/object:Gem::Dependency
112
+ name: coveralls
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: See http://www.openannotation.org/spec/core/
140
+ email:
141
+ - mbklein@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".coveralls.yml"
147
+ - ".gitignore"
148
+ - ".rspec"
149
+ - ".travis.yml"
150
+ - Gemfile
151
+ - LICENSE
152
+ - README.md
153
+ - Rakefile
154
+ - active_annotations.gemspec
155
+ - app/models/active_annotations/annotation.rb
156
+ - db/migrate/20160422052041_create_annotations.rb
157
+ - lib/active_annotations.rb
158
+ - lib/active_annotations/document_loader.rb
159
+ - lib/active_annotations/engine.rb
160
+ - lib/active_annotations/frame.yml
161
+ - lib/active_annotations/oa.jsonld
162
+ - lib/active_annotations/rdf_annotation.rb
163
+ - lib/active_annotations/version.rb
164
+ - lib/generators/active_annotations/install_generator.rb
165
+ - tasks/active_annotations.rake
166
+ homepage: http://github.com/avalonmediasystem/active_annotations
167
+ licenses:
168
+ - Apache
169
+ metadata: {}
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubyforge_project:
186
+ rubygems_version: 2.5.1
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: OpenAnnontations + JSON-LD + ActiveRecord
190
+ test_files: []