rdf-virtuoso 0.0.15 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -18,7 +18,11 @@ This example assumes you have a local installation of Virtoso running at standar
18
18
 
19
19
  uri = "http://localhost:8890/sparql"
20
20
  update_uri = "http://localhost:8890/sparql-auth"
21
- repo = RDF::Virtuoso::Repository.new(uri, :update_uri => update_uri, :username => 'admin', :password => 'secret', :auth_method => 'digest')
21
+ repo = RDF::Virtuoso::Repository.new(uri,
22
+ :update_uri => update_uri,
23
+ :username => 'admin',
24
+ :password => 'secret',
25
+ :auth_method => 'digest')
22
26
 
23
27
  :auth_method can be 'digest' or 'basic'. a repository connection without auth requires only uri
24
28
 
@@ -60,40 +64,3 @@ or you can dynamically add RDF::Vocabulary objects
60
64
  Results will be an array of RDF::Query::Solution that can be accessed by bindings or iterated
61
65
 
62
66
  count = result.first[:count].to_i
63
-
64
- ## Rails specifics
65
- Working on a prototype Rails application for negotiating and manipulating linked data in an RDF store, I discovered the lack of a reasonably current library to bridge the gap between the fairly well-established, modular RDF.rb library and a Rails 3 application. I wanted to be able to manipulate RDF data in a convenient, ActiveRecord/ActiveModel way. It turned out to be fairly non-trivial to mimic true AR/AM behavior and this is more or less the groundwork and result of my experimentation. I now have a much better idea of how to proceed, I just need the time to really go deep into this.
66
- An example prototype that exercises this library can be found here: https://github.com/digibib/booky
67
-
68
- It must be stressed that this is still early days, with lots of refactoring and abstraction to be done, along with very specific functionality targeted at the prototype I've been working on. So anyone wanting a more generalized approach would be well served by waiting until I'm further along.
69
-
70
- Essentially, a model in a Rails 3 app subclasses ActiveRDF::Model, which in itself includes the following modules:
71
- ActiveAttr::Model - https://github.com/cgriego/active_attr
72
- ActiveModel::Dirty - http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
73
- ActiveRDF::Persistence - responsible for handling persistence, in this gem
74
-
75
- In a nutshell:
76
-
77
- ActiveRDF::Model provides common functionality for models along with some mixed-in 3rd-party modules
78
-
79
- ActiveRDF::Persistence provides some of the functionality a Rails model may expect, along with some placeholder methods that are waiting for AR/Arel type implementations. All communication is done via the query language SPARQL, (version 1.1 as of this writing).
80
-
81
- RDF::Virtuoso::Client provides a rudimentary connection class for interacting with a Virtuoso server. It is inspired by API Smith (https://github.com/filtersquad/api_smith) which has some nice features, such as a convenient way to specify a Parser class for a given mimetype. See RDF::Virtuoso::Parser for example, and the method RDF::Virtuoso::Client#api_get.
82
-
83
- ## Challenges
84
- In no particular order:
85
-
86
- * In RDF data, the equivalent of a primary key in a relational database is called the subject (part of each of a set of triples), which is represented by a URI which looks like an absolute URL. When Rails does its magic with routes and friends, it assumes by default that it's dealing with an Integer, which is fine for a relational database id column which is auto-generated by the RDBM, unique and very often an int. Under the covers, to_i is called on id. This does not work for URLs, obviously. Also, you can't just send unencoded URLs over the wire as part of a path in a route. Anyway, this whole issue needed to be solved quickly, so for now RDF subjects that are part of a path are encoded and then decoded before insertion.
87
- * On the topic of unique ids, relational databases can be told to autogenerate unique primary keys on insert for you. Here, we have to use a library (UUID) to generate unique ids before insert.
88
- * On the whole, when dealing with RDF subjects, care has to be taken how they are constructed. They can end with either a forward-slash or a hash, followed by some unique identifier. Seems like the hash-notation is preferred if the data is ever to be be exported as turtle files (suffix ttl) and the unique identifier potentially starts with an integer. Whether that is a bug in the writer implementation is unclear, but better safe than sorry.
89
- * An update in an RDF store is a so-called modify and consists of one or more delete statements followed by one or more insert statements. Meaning that when a triple has changed, the original has to be deleted before the modified triple is inserted. There is a potential for leaving the data in an inconsistent state if, for instance, the delete directive isn't constructed correctly and the insert is. Or vice versa. With ActiveRecord, database writes are automatically wrapped in transactions that will roll back the data on failure. This too needed to be implemented explicitly, as seen in ActiveRDF::Persistence#update_attributes, using the SimpleTransaction libray.
90
-
91
- ## Notes
92
- The following classes are not yet in use or will probably disapper:
93
-
94
- * ActiveRDF::AssociationReflection
95
- * ActiveRDF::Exceptions
96
- * ActiveRDF::Reflections
97
-
98
-
99
-
@@ -1,5 +1,5 @@
1
1
  module RDF
2
2
  module Virtuoso
3
- VERSION = "0.0.15"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdf-virtuoso
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.15
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-10-22 00:00:00.000000000 Z
13
+ date: 2012-10-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -44,102 +44,6 @@ dependencies:
44
44
  - - ~>
45
45
  - !ruby/object:Gem::Version
46
46
  version: 0.3.5
47
- - !ruby/object:Gem::Dependency
48
- name: vcr
49
- requirement: !ruby/object:Gem::Requirement
50
- none: false
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- type: :development
56
- prerelease: false
57
- version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
- requirements:
60
- - - ! '>='
61
- - !ruby/object:Gem::Version
62
- version: '0'
63
- - !ruby/object:Gem::Dependency
64
- name: webmock
65
- requirement: !ruby/object:Gem::Requirement
66
- none: false
67
- requirements:
68
- - - ! '>='
69
- - !ruby/object:Gem::Version
70
- version: '0'
71
- type: :development
72
- prerelease: false
73
- version_requirements: !ruby/object:Gem::Requirement
74
- none: false
75
- requirements:
76
- - - ! '>='
77
- - !ruby/object:Gem::Version
78
- version: '0'
79
- - !ruby/object:Gem::Dependency
80
- name: activesupport
81
- requirement: !ruby/object:Gem::Requirement
82
- none: false
83
- requirements:
84
- - - ~>
85
- - !ruby/object:Gem::Version
86
- version: 3.2.3
87
- type: :runtime
88
- prerelease: false
89
- version_requirements: !ruby/object:Gem::Requirement
90
- none: false
91
- requirements:
92
- - - ~>
93
- - !ruby/object:Gem::Version
94
- version: 3.2.3
95
- - !ruby/object:Gem::Dependency
96
- name: active_attr
97
- requirement: !ruby/object:Gem::Requirement
98
- none: false
99
- requirements:
100
- - - ! '>='
101
- - !ruby/object:Gem::Version
102
- version: '0'
103
- type: :runtime
104
- prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- none: false
107
- requirements:
108
- - - ! '>='
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
- - !ruby/object:Gem::Dependency
112
- name: uuid
113
- requirement: !ruby/object:Gem::Requirement
114
- none: false
115
- requirements:
116
- - - ~>
117
- - !ruby/object:Gem::Version
118
- version: 2.3.5
119
- type: :runtime
120
- prerelease: false
121
- version_requirements: !ruby/object:Gem::Requirement
122
- none: false
123
- requirements:
124
- - - ~>
125
- - !ruby/object:Gem::Version
126
- version: 2.3.5
127
- - !ruby/object:Gem::Dependency
128
- name: transaction-simple
129
- requirement: !ruby/object:Gem::Requirement
130
- none: false
131
- requirements:
132
- - - ~>
133
- - !ruby/object:Gem::Version
134
- version: 1.4.0
135
- type: :runtime
136
- prerelease: false
137
- version_requirements: !ruby/object:Gem::Requirement
138
- none: false
139
- requirements:
140
- - - ~>
141
- - !ruby/object:Gem::Version
142
- version: 1.4.0
143
47
  - !ruby/object:Gem::Dependency
144
48
  name: rdf
145
49
  requirement: !ruby/object:Gem::Requirement
@@ -188,8 +92,7 @@ dependencies:
188
92
  - - ~>
189
93
  - !ruby/object:Gem::Version
190
94
  version: 1.2.0
191
- description: An RDF.rb extension library for interacting with a Virtuoso rdf store,
192
- also with ActiveRDF Rails connector
95
+ description: An RDF.rb extension library for interacting with a Virtuoso rdf store
193
96
  email:
194
97
  - benjamin.rokseth@kul.oslo.kommune.no
195
98
  executables: []
@@ -197,25 +100,16 @@ extensions: []
197
100
  extra_rdoc_files: []
198
101
  files:
199
102
  - README.md
200
- - lib/rdf/virtuoso.rb
201
103
  - lib/rdf/virtuoso/version.rb
202
- - lib/rdf/virtuoso/parser.rb
104
+ - lib/rdf/virtuoso/query.rb
203
105
  - lib/rdf/virtuoso/prefixes.rb
204
106
  - lib/rdf/virtuoso/repository.rb
205
- - lib/rdf/virtuoso/query.rb
206
- - lib/active_rdf.rb
207
- - lib/active_rdf/version.rb
208
- - lib/active_rdf/reflections.rb
209
- - lib/active_rdf/association_reflection.rb
210
- - lib/active_rdf/persistence.rb
211
- - lib/active_rdf/errors.rb
212
- - lib/active_rdf/model.rb
213
- - lib/active_rdf/exceptions.rb
107
+ - lib/rdf/virtuoso/parser.rb
108
+ - lib/rdf/virtuoso.rb
214
109
  - spec/repository_spec.rb
110
+ - spec/prefixes_spec.rb
215
111
  - spec/query_spec.rb
216
112
  - spec/spec_helper.rb
217
- - spec/active_rdf/persistence_spec.rb
218
- - spec/prefixes_spec.rb
219
113
  homepage: https://github.com/digibib/rdf-virtuoso
220
114
  licenses: []
221
115
  post_install_message:
data/lib/active_rdf.rb DELETED
@@ -1,14 +0,0 @@
1
- require 'active_support'
2
- require 'active_model'
3
- require 'active_attr'
4
- require 'active_rdf/exceptions'
5
- require 'active_rdf/errors'
6
- require 'active_rdf/version'
7
-
8
- module ActiveRDF
9
- extend ActiveSupport::Autoload
10
-
11
- autoload :Model
12
- autoload :Persistence
13
- autoload :Reflection
14
- end
@@ -1,26 +0,0 @@
1
- class AssociationReflection
2
- attr_reader :macro
3
- attr_reader :name
4
- attr_reader :options
5
-
6
- def initialize(macro, name, options = {})
7
- @macro = macro
8
- @name = name
9
- @options = options
10
- end
11
-
12
- def class_name
13
- @class_name ||= (options[:type] || derive_class_name).to_s
14
- end
15
-
16
- def klass
17
- @klass ||= class_name.constantize
18
- end
19
-
20
- private
21
-
22
- def derive_class_name
23
- name.to_s.camelize
24
- end
25
- end
26
-
@@ -1,9 +0,0 @@
1
- module ActiveRDF
2
-
3
- # Base class for all ActiveRDF errors
4
- class ActiveRDFError < StandardError
5
- end
6
-
7
- class ResourceNotFoundError < ActiveRDFError
8
- end
9
- end
@@ -1,83 +0,0 @@
1
- module ActiveRDF
2
-
3
- class ConnectionError < StandardError # :nodoc:
4
- attr_reader :response
5
-
6
- def initialize(response, message = nil)
7
- @response = response
8
- @message = message
9
- end
10
-
11
- def to_s
12
- message = "Failed."
13
- message << " Response code = #{response.code}." if response.respond_to?(:code)
14
- message << " Response message = #{response.message}." if response.respond_to?(:message)
15
- message
16
- end
17
- end
18
-
19
- # Raised when a Timeout::Error occurs.
20
- class TimeoutError < ConnectionError
21
- def initialize(message)
22
- @message = message
23
- end
24
- def to_s; @message ;end
25
- end
26
-
27
- # Raised when a OpenSSL::SSL::SSLError occurs.
28
- class SSLError < ConnectionError
29
- def initialize(message)
30
- @message = message
31
- end
32
- def to_s; @message ;end
33
- end
34
-
35
- # 3xx Redirection
36
- class Redirection < ConnectionError # :nodoc:
37
- def to_s
38
- response['Location'] ? "#{super} => #{response['Location']}" : super
39
- end
40
- end
41
-
42
- class MissingPrefixParam < ArgumentError # :nodoc:
43
- end
44
-
45
- # 4xx Client Error
46
- class ClientError < ConnectionError # :nodoc:
47
- end
48
-
49
- # 400 Bad Request
50
- class BadRequest < ClientError # :nodoc:
51
- end
52
-
53
- # 401 Unauthorized
54
- class UnauthorizedAccess < ClientError # :nodoc:
55
- end
56
-
57
- # 403 Forbidden
58
- class ForbiddenAccess < ClientError # :nodoc:
59
- end
60
-
61
- # 404 Not Found
62
- class ResourceNotFound < ClientError # :nodoc:
63
- end
64
-
65
- # 409 Conflict
66
- class ResourceConflict < ClientError # :nodoc:
67
- end
68
-
69
- # 410 Gone
70
- class ResourceGone < ClientError # :nodoc:
71
- end
72
-
73
- # 5xx Server Error
74
- class ServerError < ConnectionError # :nodoc:
75
- end
76
-
77
- # 405 Method Not Allowed
78
- class MethodNotAllowed < ClientError # :nodoc:
79
- def allowed_methods
80
- @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
81
- end
82
- end
83
- end
@@ -1,69 +0,0 @@
1
- require 'uuid'
2
- require 'transaction/simple'
3
- require 'rdf'
4
- require 'active_rdf/reflections'
5
-
6
- module ActiveRDF
7
-
8
- class Model
9
- include ActiveAttr::Model
10
- include ActiveModel::Dirty
11
- include ActiveRDF::Persistence
12
-
13
- # All children should have these attributes
14
- attribute :id, type: String
15
- attribute :subject, type: String
16
-
17
-
18
- class << self
19
- attr_accessor :reflections
20
-
21
- def graph
22
- url = RDF::URI.new("http://data.deichman.no")
23
- if defined?(Rails)
24
- url = url.join Rails.env unless (Rails.env.production? || Rails.env.staging?)
25
- end
26
- url / self.name.downcase.pluralize
27
- end
28
-
29
- def encode(string)
30
- [string].pack('m0')
31
- end
32
-
33
- def decode(string)
34
- string.unpack('m')[0]
35
- end
36
-
37
- def from_param(param)
38
- decode param
39
- end
40
-
41
- private
42
-
43
- def inherited(child)
44
- child.instance_variable_set :@reflections, @reflections.dup
45
- super
46
- end
47
- end # Class methods
48
-
49
- def type
50
- self.class.type
51
- end
52
-
53
- # When using an object's subject, which comes in the format http://example.org/object#123 as
54
- # a query param, we must encode it first
55
- def to_param
56
- self.class.encode self.subject
57
- end
58
-
59
- def graph
60
- self.class.graph
61
- end
62
-
63
- extend Reflections
64
-
65
- @reflections = HashWithIndifferentAccess.new
66
-
67
- end
68
-
69
- end
@@ -1,185 +0,0 @@
1
- module ActiveRDF
2
- module Persistence
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
-
7
- # Override ActiveAttr::Attributes.attribute=(name, value)
8
- def attribute=(name, value)
9
- @attributes ||= {}
10
- # We'll assume that nil and "" are equivalent
11
- unless (@attributes[name].blank? && value.blank?) || (@attributes[name] == value)
12
- send("#{name}_will_change!")
13
- end
14
- @attributes[name] = value
15
- end
16
- end
17
-
18
- module ClassMethods
19
- def connection
20
- # TODO: make this behave like AM/AR Connection
21
- #CLIENT
22
- REPOSITORY
23
- end
24
-
25
- def create(attrs = nil)
26
- object = new(attrs)
27
- object.save
28
- object
29
- end
30
-
31
- def create!(attrs = nil)
32
- object = new(attrs)
33
- object.save!
34
- object
35
- end
36
-
37
- def before_create(method)
38
- # Placeholder until implemented in ActiveRDF::Callbacks
39
- end
40
-
41
- def before_save(method)
42
- # Placeholder until implemented in ActiveRDF::Callbacks
43
- end
44
-
45
- # @see: http://rdf.rubyforge.org/RDF/Query/Solutions.html
46
- def order(variable)
47
- end
48
-
49
- def where(conditions)
50
- end
51
-
52
- def scope(variable, conditions)
53
- end
54
-
55
- def scoped
56
- end
57
-
58
- def count
59
- #query = "SELECT COUNT(DISTINCT ?s) WHERE { GRAPH <#{self.graph}> { ?s a <#{self.type}> }}"
60
- query = RDF::Virtuoso::Query.select(:s).count(:s).distinct.where([:s, RDF.type, self.type ])
61
- result = connection.select(query)
62
- result.first[:count].to_i
63
- end
64
-
65
- def find(object_or_id, conditions = {})
66
-
67
- subject = case object_or_id
68
- when String then decode(object_or_id)
69
- when self then object_or_id.subject
70
- else raise ActiveModel::MissingAttributeError.new(object_or_id.inspect)
71
- end
72
-
73
- find_by_subject(subject, conditions = {})
74
- end
75
-
76
- def first
77
- all(limit: 1).first
78
- end
79
-
80
- # What does this do?
81
- def execute(sql)
82
- results = []
83
- solutions = connection.select(sql)
84
- solutions.each do |solution|
85
- record = new
86
- solution.each_binding do |name, value|
87
- record[name] = value.to_s
88
- end
89
- if record.subject.present?
90
- record.id = id_for(record.subject)
91
- record.changed_attributes.clear
92
- end
93
- results << record
94
- end
95
- results
96
- end
97
-
98
- # TODO: set baseurl via config
99
- def subject_for(id)
100
- RDF::URI('http://data.deichman.no') / self.name.downcase / "/id_" / id
101
- end
102
-
103
- def id_for(subject)
104
- subject.to_s.split("/").last.gsub('id_', '')
105
- end
106
-
107
- def destroy_all
108
- #query = "DELETE FROM <#{self.graph}> { ?s ?p ?o } WHERE { GRAPH <#{self.graph}> { ?s a <#{self.type}> . ?s ?p ?o } }"
109
- query = RDF::Virtuoso::Query.delete([:s, :p, :o]).graph(self.graph).where([:s, RDF.type, self.type],[:s, :p, :o])
110
- connection.delete(query)
111
- end
112
-
113
- end
114
-
115
- # Instance methods
116
-
117
- def connection
118
- self.class.connection
119
- end
120
-
121
- def save
122
- return false unless self.valid?
123
- create_or_update
124
- end
125
-
126
- def save!
127
- unless self.valid?
128
- raise ActiveRecord::RecordInvalid.new(self)
129
- end
130
- create_or_update
131
- end
132
-
133
- def destroy
134
- subject = subject_for(self.id)
135
- #p subject
136
- query = RDF::Virtuoso::Query.delete([subject, :p, :o]).graph(graph).where([subject, :p, :o])
137
- result = connection.delete(query)
138
- end
139
-
140
-
141
- def update_attributes(attributes)
142
- self.extend(::Transaction::Simple)
143
- status = false
144
- begin
145
- self.start_transaction
146
- self.assign_attributes(attributes)
147
- status = save
148
- self.commit_transaction
149
- rescue Exception
150
- self.rewind_transaction
151
- self.abort_transaction
152
- end
153
- status
154
- end
155
-
156
- def reload
157
- self.attributes = self.class.find(self).attributes
158
- self
159
- end
160
-
161
- def new_record?
162
- self.id.nil?
163
- end
164
-
165
- def persisted?
166
- !new_record?
167
- end
168
-
169
- def subject_for(id)
170
- self.class.subject_for(id)
171
- end
172
-
173
- private
174
-
175
- def create_or_update
176
- result = new_record? ? create : update
177
- result != false
178
- end
179
-
180
- def guid
181
- UUID.generate(:compact)
182
- end
183
-
184
- end
185
- end
@@ -1,19 +0,0 @@
1
- require 'active_rdf/association_reflection'
2
-
3
- module ActiveRDF
4
- module Reflections
5
- # Returns a hash containing all AssociationReflection objects for the current class
6
- # Example:
7
- #
8
- # Invoice.reflections
9
- # Account.reflections
10
- #
11
- def reflections
12
- read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
13
- end
14
-
15
- def reflect_on_association(association)
16
- reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
17
- end
18
- end
19
- end
@@ -1,10 +0,0 @@
1
- module ActiveRDF
2
- module VERSION #:nodoc:
3
- MAJOR = 0
4
- MINOR = 0
5
- TINY = 15
6
- PRE = nil
7
-
8
- STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
- end
10
- end
@@ -1,31 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class Resource < ActiveRDF::Model
4
- end
5
-
6
- describe ActiveRDF::Persistence do
7
-
8
- let(:resource) { Resource.new }
9
-
10
- before do
11
- resource.stub(:id).and_return("some_unique_id")
12
- client = double("client")
13
- resource.stub(:connection).and_return client
14
- end
15
-
16
- describe :destroy do
17
-
18
- it "formats the destroy query correctly" do
19
- subject = resource.subject_for(resource.id)
20
- # query =
21
- #<<-q
22
- #DELETE FROM <#{resource.graph}> { <#{subject}> ?p ?o }
23
- #WHERE { <#{subject}> ?p ?o }
24
- #q
25
- query = RDF::Virtuoso::Query.delete([subject, :p, :o]).graph(resource.graph).where([subject, :p, :o])
26
- resource.connection.should_receive(:delete).with(query)
27
- resource.destroy
28
- end
29
- end
30
-
31
- end