gearbox 0.1.0 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -3
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/bin/gearbox +9 -0
- data/gearbox.gemspec +134 -0
- data/lib/gearbox.rb +124 -5
- data/lib/gearbox/attribute.rb +128 -0
- data/lib/gearbox/mixins/active_model_implementation.rb +27 -0
- data/lib/gearbox/mixins/resource.rb +20 -4
- data/lib/gearbox/mixins/semantic_accessors.rb +128 -89
- data/lib/gearbox/mixins/subject_methods.rb +88 -0
- data/lib/gearbox/rdf_collection.rb +22 -8
- data/lib/gearbox/types.rb +9 -8
- data/lib/gearbox/vocabulary.rb +149 -0
- data/lib/pry_utilities.rb +71 -0
- data/scratch/4s.rb +335 -0
- data/scratch/DEVELOPMENT_NOTES.md +85 -0
- data/scratch/actionable.md +34 -0
- data/scratch/ccrdf.html-rdfa.nq +100 -0
- data/scratch/foo.rb +17 -0
- data/scratch/index.rdf +7932 -0
- data/scratch/j2.rb +10 -0
- data/scratch/junk.rb +16 -0
- data/scratch/out.rb +67 -0
- data/spec/gearbox/attribute_spec.rb +455 -0
- data/spec/gearbox/mixins/active_model_implementation_spec.rb +18 -0
- data/spec/gearbox/mixins/ad_hoc_properties_spec.rb +44 -44
- data/spec/gearbox/mixins/resource_spec.rb +47 -8
- data/spec/gearbox/mixins/semantic_accessors_spec.rb +72 -43
- data/spec/gearbox/mixins/subject_methods_spec.rb +126 -0
- data/spec/gearbox/rdf_collection_spec.rb +28 -2
- data/spec/gearbox_spec.rb +6 -2
- data/spec/spec_helper.rb +1 -0
- metadata +150 -42
- data/Gemfile.lock +0 -138
- data/lib/examples/audience.rb +0 -24
- data/lib/examples/person.rb +0 -29
- data/lib/examples/reference.rb +0 -38
- data/lib/examples/theme.rb +0 -8
- data/spec/examples/audience_spec.rb +0 -28
- data/spec/examples/person_spec.rb +0 -45
- data/spec/examples/reference_spec.rb +0 -43
- data/spec/examples/theme_spec.rb +0 -137
data/lib/gearbox/types.rb
CHANGED
@@ -14,14 +14,15 @@ module Gearbox
|
|
14
14
|
# No autoloading here--the associations to XSD types are made by the
|
15
15
|
# classes themselves, so we need to explicitly require them or XSD types
|
16
16
|
# will show up as not found.
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
17
|
+
require_relative 'types/integer'
|
18
|
+
require_relative 'types/boolean'
|
19
|
+
require_relative 'types/any'
|
20
|
+
require_relative 'types/string'
|
21
|
+
require_relative 'types/float'
|
22
|
+
require_relative 'types/uri'
|
23
|
+
require_relative 'types/decimal'
|
24
|
+
require_relative 'types/native'
|
25
|
+
require_relative 'types/date'
|
25
26
|
|
26
27
|
|
27
28
|
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
module Gearbox
|
6
|
+
=begin
|
7
|
+
##
|
8
|
+
# Derived from RDF::Vocabulary
|
9
|
+
# However, I have two new use cases for Gearbox::Vocabulary
|
10
|
+
# * as a single source for an ontology
|
11
|
+
# * as a base_uri in a model
|
12
|
+
|
13
|
+
## Single source Ontology
|
14
|
+
Ontologies have classes and attributes. That means there is some nesting in the vocabulary.
|
15
|
+
We have this
|
16
|
+
|
17
|
+
* /:collection/:item/:sub-collection/:item
|
18
|
+
* concatenating the existing identifier or key with a suitable base URI.
|
19
|
+
* /:collection/:id
|
20
|
+
|
21
|
+
http://www.bbc.co.uk/music/artists/a74b1b7f-71a5-4011-9441-d0b5e4122711
|
22
|
+
http://musicbrainz.org/artist/a74b1b7f-71a5-4011-9441-d0b5e4122711
|
23
|
+
|
24
|
+
<http:www.example.org/category/heavy-metal>
|
25
|
+
rdfs:label "Heavy Metal"
|
26
|
+
|
27
|
+
=end
|
28
|
+
|
29
|
+
|
30
|
+
# But with some patterns in mind from http://patterns.dataincubator.org/book/
|
31
|
+
|
32
|
+
|
33
|
+
class Vocabulary < RDF::Vocabulary
|
34
|
+
|
35
|
+
# =================
|
36
|
+
# = Class Methods =
|
37
|
+
# =================
|
38
|
+
|
39
|
+
class << self
|
40
|
+
|
41
|
+
##
|
42
|
+
# Defines a vocabulary term called `property`.
|
43
|
+
#
|
44
|
+
# @param [Symbol]
|
45
|
+
# @return [void]
|
46
|
+
def property(property, opts={})
|
47
|
+
metaclass = class << self; self; end
|
48
|
+
metaclass.send(:define_method, property) { self.lookup(property) } # class method
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Returns the URI for the term `property` in this vocabulary.
|
53
|
+
#
|
54
|
+
# @param [#to_s] property
|
55
|
+
# @return [RDF::URI]
|
56
|
+
def [](property)
|
57
|
+
RDF::URI.intern([to_s, property.to_s].join(''))
|
58
|
+
end
|
59
|
+
|
60
|
+
def lookup(property)
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
protected
|
66
|
+
def create(uri) # @private
|
67
|
+
@@uri = uri
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def inherited(subclass) # @private
|
72
|
+
@@subclasses << subclass
|
73
|
+
unless @@uri.nil?
|
74
|
+
subclass.send(:private_class_method, :new)
|
75
|
+
@@uris[subclass] = @@uri
|
76
|
+
@@uri = nil
|
77
|
+
end
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
def method_missing(property, *args, &block)
|
82
|
+
if args.empty? && @@uris.has_key?(self)
|
83
|
+
self[property]
|
84
|
+
else
|
85
|
+
super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# @param [RDF::URI, String, #to_s]
|
93
|
+
def initialize(uri)
|
94
|
+
@uri = case uri
|
95
|
+
when RDF::URI then uri.to_s
|
96
|
+
else RDF::URI.parse(uri.to_s) ? uri.to_s : nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Returns the URI for the term `property` in this vocabulary.
|
102
|
+
#
|
103
|
+
# @param [#to_s] property
|
104
|
+
# @return [URI]
|
105
|
+
def [](property)
|
106
|
+
RDF::URI.intern([to_s, property.to_s].join(''))
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Returns the base URI for this vocabulary.
|
111
|
+
#
|
112
|
+
# @return [URI]
|
113
|
+
def to_uri
|
114
|
+
RDF::URI.intern(to_s)
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Returns a string representation of this vocabulary.
|
119
|
+
#
|
120
|
+
# @return [String]
|
121
|
+
def to_s
|
122
|
+
@uri.to_s
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Returns a developer-friendly representation of this vocabulary.
|
127
|
+
#
|
128
|
+
# @return [String]
|
129
|
+
def inspect
|
130
|
+
sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
|
135
|
+
def method_missing(property, *args, &block)
|
136
|
+
if args.empty?
|
137
|
+
self[property]
|
138
|
+
else
|
139
|
+
raise ArgumentError.new("wrong number of arguments (#{args.size} for 0)")
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
@@subclasses = [::RDF] # @private
|
146
|
+
@@uris = {} # @private
|
147
|
+
@@uri = nil # @private
|
148
|
+
end # Vocabulary
|
149
|
+
end # RDF
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Useful for a Pry session.
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
include Gearbox
|
6
|
+
|
7
|
+
class Utilities
|
8
|
+
# Great for writing ad hoc models.
|
9
|
+
# TODO: Make this work for several sessions. (Thread it?)
|
10
|
+
def write_model(name)
|
11
|
+
raise "Directory does not exist" unless File.exist?(model_directory)
|
12
|
+
filename = File.join(model_directory, "#{name}.rb")
|
13
|
+
raise "ENV['EDITOR'] not set" unless ENV['EDITOR']
|
14
|
+
`#{ENV['EDITOR']} #{filename}`
|
15
|
+
load filename
|
16
|
+
end
|
17
|
+
alias :update_model :write_model
|
18
|
+
alias :build_model :write_model
|
19
|
+
|
20
|
+
def load_model(name)
|
21
|
+
raise "Directory does not exist" unless File.exist?(model_directory)
|
22
|
+
filename = File.join(model_directory, "#{name}.rb")
|
23
|
+
load filename
|
24
|
+
end
|
25
|
+
|
26
|
+
def model_directory
|
27
|
+
@model_directory ||= "/tmp"
|
28
|
+
end
|
29
|
+
attr_writer :model_directory
|
30
|
+
|
31
|
+
def tmp_directory
|
32
|
+
@tmp_directory ||= "/tmp"
|
33
|
+
end
|
34
|
+
attr_writer :tmp_directory
|
35
|
+
|
36
|
+
require 'fileutils'
|
37
|
+
# Great for writing descriptions without messing around with quotes and escapes and things
|
38
|
+
# TODO: Make this work for several sessions. (Thread it?)
|
39
|
+
def get_note(type="md")
|
40
|
+
contents = nil
|
41
|
+
begin
|
42
|
+
filename = File.join(tmp_directory, "#{self.object_id}.#{type}")
|
43
|
+
i = 0
|
44
|
+
while File.exist?(filename)
|
45
|
+
filename = File.join(tmp_directory, "#{self.object_id}#{i}.#{type}")
|
46
|
+
i += 1
|
47
|
+
end
|
48
|
+
raise "ENV['EDITOR'] not set" unless ENV['EDITOR']
|
49
|
+
`#{ENV['EDITOR']} #{filename}`
|
50
|
+
contents = File.read(filename)
|
51
|
+
ensure
|
52
|
+
puts "Cleaning up temp file and exiting ..."
|
53
|
+
FileUtils.rm_f(filename)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
@utilities = Utilities.new
|
60
|
+
extend Forwardable
|
61
|
+
def_delegators :@utilities,
|
62
|
+
:write_model,
|
63
|
+
:update_model,
|
64
|
+
:build_model,
|
65
|
+
:model_directory,
|
66
|
+
:model_directory=,
|
67
|
+
:tmp_directory,
|
68
|
+
:tmp_directory=,
|
69
|
+
:get_note,
|
70
|
+
:load_model
|
71
|
+
|
data/scratch/4s.rb
ADDED
@@ -0,0 +1,335 @@
|
|
1
|
+
require 'linkeddata'
|
2
|
+
require 'rest_client'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'pry'
|
5
|
+
|
6
|
+
# endpoint = "http://localhost:8000/data/"
|
7
|
+
# stmt = <<-END
|
8
|
+
# INSERT DATA
|
9
|
+
# {
|
10
|
+
# <http://example.org/subject> <http://example.org/predicate> <http://example.org/object>
|
11
|
+
# }
|
12
|
+
# END
|
13
|
+
# response = RestClient.put endpoint, stmt, :content_type => "application/rdf+xml"
|
14
|
+
# graph = 'http://source.data.gov.uk/data/reference/organogram-co/2010-06-30'
|
15
|
+
# binding.pry
|
16
|
+
# response = RestClient.put endpoint + graph, stmt, :content_type => "application/rdf+xml"
|
17
|
+
|
18
|
+
def load_graph
|
19
|
+
|
20
|
+
endpoint = "http://localhost:8000/data/"
|
21
|
+
filename = File.expand_path("../index.rdf", __FILE__)
|
22
|
+
graph = 'http://source.data.gov.uk/data/reference/organogram-co/2010-06-30'
|
23
|
+
response = RestClient.put endpoint + graph, File.read(filename), :content_type => "application/rdf+xml"
|
24
|
+
# response = RestClient.put endpoint, File.read(filename), :content_type => "application/rdf+xml"
|
25
|
+
puts "Response: #{response.code}", response.to_str
|
26
|
+
end
|
27
|
+
|
28
|
+
def count_triples
|
29
|
+
endpoint = "http://localhost:8000/sparql/"
|
30
|
+
# sparql = "SELECT (COUNT(DISTINCT ?s) AS ?count) WHERE { ?s ?p ?o } LIMIT 10"
|
31
|
+
sparql = "SELECT * WHERE { ?s ?p ?o } LIMIT 10"
|
32
|
+
response = RestClient.post endpoint, :query => sparql
|
33
|
+
xml = Nokogiri::XML(response.to_str)
|
34
|
+
end
|
35
|
+
|
36
|
+
def ordered_variables
|
37
|
+
endpoint = "http://localhost:8000/sparql/"
|
38
|
+
sparql = "SELECT (COUNT(DISTINCT ?s) AS ?count) WHERE { ?s ?p ?o } LIMIT 10"
|
39
|
+
# sparql = "SELECT * WHERE { ?s ?p ?o } LIMIT 10"
|
40
|
+
response = RestClient.post endpoint, :query => sparql
|
41
|
+
xml = Nokogiri::XML(response.to_str)
|
42
|
+
found = xml.xpath("//sparql:head/sparql:variable/@name", "sparql" => "http://www.w3.org/2005/sparql-results#").map(&:value)
|
43
|
+
found
|
44
|
+
# xml.xpath("//sparql:head/sparql:variable", "sparql" => "http://www.w3.org/2005/sparql-results#").map {|e| e.attr('name')}
|
45
|
+
end
|
46
|
+
|
47
|
+
# result = ordered_variables
|
48
|
+
|
49
|
+
|
50
|
+
# Examples of SPARQL Update 1.1 for 4Store:
|
51
|
+
# http://4store.org/trac/wiki/SparqlServer
|
52
|
+
|
53
|
+
# endpoint = "http://localhost:8000/sparql/"
|
54
|
+
# You can use sparql-query to query the SPARQL server on the command line.
|
55
|
+
|
56
|
+
|
57
|
+
# Soft limit of about 1000, what is that? triples? seconds?
|
58
|
+
|
59
|
+
class NotImplemented < StandardError; end
|
60
|
+
|
61
|
+
# Geared towards interpreting whatever results we get back from a SPARQL result
|
62
|
+
class SPARQLResult
|
63
|
+
|
64
|
+
SPARQL_NAMESPACE = {'sparql' => 'http://www.w3.org/2005/sparql-results#'} unless defined?(SPARQL_NAMESPACE)
|
65
|
+
attr_reader :result_string
|
66
|
+
|
67
|
+
def initialize(result_string)
|
68
|
+
@result_string = result_string
|
69
|
+
end
|
70
|
+
|
71
|
+
def ask?
|
72
|
+
not xml.xpath('//sparql:boolean', SPARQL_NAMESPACE).empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
def ask_value
|
76
|
+
return nil unless ask?
|
77
|
+
xml.xpath('//sparql:boolean', SPARQL_NAMESPACE).text == 'true'
|
78
|
+
end
|
79
|
+
|
80
|
+
def results
|
81
|
+
return ask_value if ask?
|
82
|
+
@results = Hash.new {|h, k| h[k] = []}
|
83
|
+
xml.xpath('//sparql:result', SPARQL_NAMESPACE).each do |result|
|
84
|
+
result.xpath('./sparql:binding', SPARQL_NAMESPACE).each do |variable_binding|
|
85
|
+
name = variable_binding.attr('name')
|
86
|
+
value = extract_value(variable_binding)
|
87
|
+
@results[name] << value
|
88
|
+
end
|
89
|
+
end
|
90
|
+
@results
|
91
|
+
end
|
92
|
+
|
93
|
+
def result_table
|
94
|
+
return ask_value if ask?
|
95
|
+
keys = head_node.xpath(".//sparql:variable/@name", SPARQL_NAMESPACE).map(&:value)
|
96
|
+
values = []
|
97
|
+
xml.xpath('//sparql:result', SPARQL_NAMESPACE).each do |result|
|
98
|
+
record = []
|
99
|
+
result.xpath('./sparql:binding', SPARQL_NAMESPACE).each do |variable_binding|
|
100
|
+
value = extract_value(variable_binding)
|
101
|
+
record << value
|
102
|
+
end
|
103
|
+
values << record
|
104
|
+
end
|
105
|
+
# puts keys.inspect
|
106
|
+
# values.each {|v| puts v.inspect}
|
107
|
+
values.unshift keys
|
108
|
+
values
|
109
|
+
end
|
110
|
+
|
111
|
+
def inspect
|
112
|
+
# "SPARQLResult: #{result_string[0..50].split(/\n/)[0]}"
|
113
|
+
"SPARQLResult: #{results.keys}"
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def extract_value(variable_binding_node)
|
119
|
+
# Not Implemented: literals, sequences, indices, data types, languages
|
120
|
+
# Need to deal with bnode, uri, or literal. Ignoring all of that for now...
|
121
|
+
variable_binding_node.text
|
122
|
+
end
|
123
|
+
|
124
|
+
def head_node
|
125
|
+
@head_node ||= xml.xpath('//sparql:head', SPARQL_NAMESPACE)
|
126
|
+
end
|
127
|
+
|
128
|
+
def variable_nodes
|
129
|
+
@variable_nodes ||= head_node.xpath('//sparql::variable', SPARQL_NAMESPACE)
|
130
|
+
end
|
131
|
+
|
132
|
+
def xml
|
133
|
+
@xml ||= Nokogiri::XML(result_string)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
# response = RestClient.put endpoint + graph, File.read(filename), :content_type => "application/rdf+xml"
|
139
|
+
|
140
|
+
def exercise_sparql_result
|
141
|
+
endpoint = "http://localhost:8000/sparql/"
|
142
|
+
sparql = "SELECT * WHERE { ?s ?p ?o } LIMIT 10"
|
143
|
+
# sparql = "ASK { ?s ?p ?o }"
|
144
|
+
response = RestClient.post endpoint, :query => sparql
|
145
|
+
s = SPARQLResult.new(response)
|
146
|
+
s.results
|
147
|
+
end
|
148
|
+
|
149
|
+
# puts exercise_sparql_result.inspect
|
150
|
+
|
151
|
+
# TODO: Not dealing with the soft limit yet
|
152
|
+
|
153
|
+
# Need to figure out how to write a finder for a model...
|
154
|
+
# 1) load the data in the graph
|
155
|
+
# 2) write the query
|
156
|
+
# 3) start to figure out the different ways to construct this...
|
157
|
+
def constructing_model_sparql
|
158
|
+
end
|
159
|
+
|
160
|
+
class SPARQLEndpoint
|
161
|
+
|
162
|
+
attr_reader :base_uri
|
163
|
+
|
164
|
+
def initialize(uri="http://localhost:8000")
|
165
|
+
@base_uri = uri
|
166
|
+
end
|
167
|
+
|
168
|
+
attr_writer :select_uri
|
169
|
+
def select_uri
|
170
|
+
@select_uri ||= File.join(base_uri, 'sparql/')
|
171
|
+
end
|
172
|
+
|
173
|
+
attr_writer :update_uri
|
174
|
+
def update_uri
|
175
|
+
@update_uri ||= File.join(base_uri, 'data/')
|
176
|
+
end
|
177
|
+
|
178
|
+
def query(sparql)
|
179
|
+
response = RestClient.post select_uri, :query => sparql
|
180
|
+
SPARQLResult.new(response)
|
181
|
+
end
|
182
|
+
|
183
|
+
def queries
|
184
|
+
registered.keys
|
185
|
+
end
|
186
|
+
|
187
|
+
def memoize_query(name, sparql=nil)
|
188
|
+
return memoized[name] if memoized[name]
|
189
|
+
register_query(name, sparql)
|
190
|
+
memoized[name] ||= query(sparql)
|
191
|
+
end
|
192
|
+
alias :memoize :memoize_query
|
193
|
+
|
194
|
+
def register_query(name, sparql)
|
195
|
+
registered[name] = sparql
|
196
|
+
memoized[name] = nil
|
197
|
+
self.class.send(:define_method, name) do
|
198
|
+
memoized[name] ||= query(registered[name])
|
199
|
+
end
|
200
|
+
true
|
201
|
+
end
|
202
|
+
alias :register :register_query
|
203
|
+
|
204
|
+
def registered
|
205
|
+
@registered ||= {}
|
206
|
+
end
|
207
|
+
|
208
|
+
def memoized
|
209
|
+
@memoized ||= {}
|
210
|
+
end
|
211
|
+
|
212
|
+
def inspect
|
213
|
+
"SPARQLEndpoint: #{base_uri}"
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
# s = SPARQLEndpoint.new
|
219
|
+
# s.register('basic', "SELECT * WHERE { ?s ?p ?o } LIMIT 10")
|
220
|
+
# s.register 'predicates', 'select distinct ?predicate where {?s ?predicate ?o}'
|
221
|
+
# s.register 'notations', 'select distinct ?subject ?notations where {?subject <http://www.w3.org/2004/02/skos/core#notation}'
|
222
|
+
# s.register 'phones', 'select distinct ?phone where {?s <http://xmlns.com/foaf/0.1/phone> ?phone}'
|
223
|
+
# binding.pry
|
224
|
+
|
225
|
+
require 'fileutils'
|
226
|
+
# Note, I haven't come up with a good fallback if ENV['EDITOR'] hasn't been defined yet.
|
227
|
+
# I tried to open vim as a fallback, I ended up in non-terminal mode, had to kill processes manually.
|
228
|
+
# Will need to fork or something: http://workingwithunixprocesses.com/
|
229
|
+
def get_note(type="md")
|
230
|
+
contents = nil
|
231
|
+
begin
|
232
|
+
filename = "/tmp/#{self.object_id}.#{type}"
|
233
|
+
while File.exist?(filename)
|
234
|
+
i ||= 0
|
235
|
+
filename = "/tmp/#{self.object_id}#{i}.#{type}"
|
236
|
+
end
|
237
|
+
`#{ENV['EDITOR']} #{filename}`
|
238
|
+
contents = File.read(filename)
|
239
|
+
ensure
|
240
|
+
puts "Exiting ..."
|
241
|
+
FileUtils.rm_f(filename)
|
242
|
+
end
|
243
|
+
# contents
|
244
|
+
end
|
245
|
+
|
246
|
+
def get_model_contents(subject='http://reference.data.gov.uk/id/department/co/post/44')
|
247
|
+
s = SPARQLEndpoint.new
|
248
|
+
s.register 'model', <<-END
|
249
|
+
SELECT DISTINCT ?predicate ?value
|
250
|
+
WHERE {
|
251
|
+
{<#{subject}> ?predicate ?value}
|
252
|
+
UNION
|
253
|
+
{?value ?predicate <#{subject}>}
|
254
|
+
}
|
255
|
+
END
|
256
|
+
s.model
|
257
|
+
end
|
258
|
+
|
259
|
+
# get_model_contents
|
260
|
+
|
261
|
+
def write_model(name)
|
262
|
+
filename = "/tmp/#{name}.rb"
|
263
|
+
`#{ENV['EDITOR']} #{filename}`
|
264
|
+
load filename
|
265
|
+
end
|
266
|
+
alias :update_model :write_model
|
267
|
+
|
268
|
+
=begin
|
269
|
+
|
270
|
+
The task list says to play with more semantic models: exploratory SPARQL, SPARQL for a single model, discover data structures in other LOD. Basically, I'm trying to figure out how to be familiar with this stuff.
|
271
|
+
=end
|
272
|
+
|
273
|
+
require 'forwardable'
|
274
|
+
|
275
|
+
class Person
|
276
|
+
|
277
|
+
extend Forwardable
|
278
|
+
def_delegators :@endpoint, :white_list, :direct_attributes
|
279
|
+
|
280
|
+
attr_reader :endpoint, :id
|
281
|
+
|
282
|
+
def initialize(id="person178")
|
283
|
+
@id = id
|
284
|
+
@endpoint = SPARQLEndpoint.new
|
285
|
+
@endpoint.register :white_list, white_list_sparql
|
286
|
+
@endpoint.register :direct_attributes, direct_attributes_sparql
|
287
|
+
end
|
288
|
+
|
289
|
+
def inspect
|
290
|
+
"Person: #{URI.split(subject)[-1]}"
|
291
|
+
end
|
292
|
+
|
293
|
+
def predicate_hash
|
294
|
+
@predicate_hash ||= {
|
295
|
+
"?email" => "http://xmlns.com/foaf/0.1/mbox",
|
296
|
+
"?page" => "http://xmlns.com/foaf/0.1/page",
|
297
|
+
"?phone" => "http://xmlns.com/foaf/0.1/phone",
|
298
|
+
"?name" => "http://xmlns.com/foaf/0.1/name"
|
299
|
+
}
|
300
|
+
end
|
301
|
+
|
302
|
+
def base_uri
|
303
|
+
"http://source.data.gov.uk/data/reference/organogram-co/2010-10-31#"
|
304
|
+
end
|
305
|
+
|
306
|
+
def subject
|
307
|
+
"#{base_uri}#{id}"
|
308
|
+
end
|
309
|
+
|
310
|
+
def white_list_sparql
|
311
|
+
<<-END
|
312
|
+
SELECT *
|
313
|
+
#{white_list_where_clause}
|
314
|
+
END
|
315
|
+
end
|
316
|
+
|
317
|
+
def white_list_where_clause
|
318
|
+
@white_list_where_clause ||= <<-END
|
319
|
+
WHERE {
|
320
|
+
#{predicate_hash.map {|variable, predicate| "<#{subject}> <#{predicate}> #{variable}"}.join(" .\n")}
|
321
|
+
}
|
322
|
+
END
|
323
|
+
end
|
324
|
+
|
325
|
+
def direct_attributes_sparql
|
326
|
+
@direct_attributes_sparql ||=<<-END
|
327
|
+
SELECT DISTINCT ?predicate ?value
|
328
|
+
WHERE {
|
329
|
+
{<#{subject}> ?predicate ?value}
|
330
|
+
UNION
|
331
|
+
{?value ?predicate <#{subject}>}
|
332
|
+
}
|
333
|
+
END
|
334
|
+
end
|
335
|
+
end
|