gearbox 0.1.0 → 0.1.10

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.
Files changed (43) hide show
  1. data/Gemfile +5 -3
  2. data/Rakefile +1 -0
  3. data/VERSION +1 -1
  4. data/bin/gearbox +9 -0
  5. data/gearbox.gemspec +134 -0
  6. data/lib/gearbox.rb +124 -5
  7. data/lib/gearbox/attribute.rb +128 -0
  8. data/lib/gearbox/mixins/active_model_implementation.rb +27 -0
  9. data/lib/gearbox/mixins/resource.rb +20 -4
  10. data/lib/gearbox/mixins/semantic_accessors.rb +128 -89
  11. data/lib/gearbox/mixins/subject_methods.rb +88 -0
  12. data/lib/gearbox/rdf_collection.rb +22 -8
  13. data/lib/gearbox/types.rb +9 -8
  14. data/lib/gearbox/vocabulary.rb +149 -0
  15. data/lib/pry_utilities.rb +71 -0
  16. data/scratch/4s.rb +335 -0
  17. data/scratch/DEVELOPMENT_NOTES.md +85 -0
  18. data/scratch/actionable.md +34 -0
  19. data/scratch/ccrdf.html-rdfa.nq +100 -0
  20. data/scratch/foo.rb +17 -0
  21. data/scratch/index.rdf +7932 -0
  22. data/scratch/j2.rb +10 -0
  23. data/scratch/junk.rb +16 -0
  24. data/scratch/out.rb +67 -0
  25. data/spec/gearbox/attribute_spec.rb +455 -0
  26. data/spec/gearbox/mixins/active_model_implementation_spec.rb +18 -0
  27. data/spec/gearbox/mixins/ad_hoc_properties_spec.rb +44 -44
  28. data/spec/gearbox/mixins/resource_spec.rb +47 -8
  29. data/spec/gearbox/mixins/semantic_accessors_spec.rb +72 -43
  30. data/spec/gearbox/mixins/subject_methods_spec.rb +126 -0
  31. data/spec/gearbox/rdf_collection_spec.rb +28 -2
  32. data/spec/gearbox_spec.rb +6 -2
  33. data/spec/spec_helper.rb +1 -0
  34. metadata +150 -42
  35. data/Gemfile.lock +0 -138
  36. data/lib/examples/audience.rb +0 -24
  37. data/lib/examples/person.rb +0 -29
  38. data/lib/examples/reference.rb +0 -38
  39. data/lib/examples/theme.rb +0 -8
  40. data/spec/examples/audience_spec.rb +0 -28
  41. data/spec/examples/person_spec.rb +0 -45
  42. data/spec/examples/reference_spec.rb +0 -43
  43. data/spec/examples/theme_spec.rb +0 -137
@@ -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
- require 'gearbox/types/integer'
18
- require 'gearbox/types/boolean'
19
- require 'gearbox/types/any'
20
- require 'gearbox/types/string'
21
- require 'gearbox/types/float'
22
- require 'gearbox/types/uri'
23
- require 'gearbox/types/decimal'
24
- require 'gearbox/types/native'
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
+
@@ -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