spira 0.0.2 → 0.0.3
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.
- data/CHANGES.md +25 -0
- data/README +2 -0
- data/VERSION +1 -1
- data/lib/spira.rb +27 -0
- data/lib/spira/exceptions.rb +5 -1
- data/lib/spira/resource/class_methods.rb +37 -16
- data/lib/spira/resource/dsl.rb +5 -13
- data/lib/spira/resource/instance_methods.rb +71 -32
- data/lib/spira/version.rb +1 -1
- metadata +10 -9
data/CHANGES.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Changelog for Spira <http://github.com/datagraph/spira>
|
2
|
+
|
3
|
+
## untagged
|
4
|
+
* Bumped promise dependency to 0.1.1 to fix a Ruby 1.9 warning
|
5
|
+
* Rework error handling when a repository is not configured; this should
|
6
|
+
always now raise a Spira::NoRepositoryError regardless of what operation
|
7
|
+
was attempted, and the error message was improved as well.
|
8
|
+
* A '/' is no longer appended to base URIs ending with a '#'
|
9
|
+
* Resources can now take a BNode as a subject. Implemented #node?, #uri,
|
10
|
+
#to_uri, #to_node, and #to_subject in support of this; see the yardocs for
|
11
|
+
exact semantics. RDF::Node is monkey patched with #as, just like RDF::URI,
|
12
|
+
for instantiation. Old code should not break, but if you want to add
|
13
|
+
BNodes, you may be using #uri where you want to now be using #subject.
|
14
|
+
|
15
|
+
## 0.0.2
|
16
|
+
* Implemented #each on resource classes, allowing classes with a defined RDF
|
17
|
+
type to be enumerated
|
18
|
+
* Fragment URIs are now used as strings, allowing i.e. Integers to be used as
|
19
|
+
the final portion of a URI for classes with a base_uri defined.
|
20
|
+
* Added an RDF::URI property type
|
21
|
+
* Implemented #to_rdf and #to_uri for increased compatibility with the RDF.rb
|
22
|
+
ecosystem
|
23
|
+
|
24
|
+
## 0.0.1
|
25
|
+
* Initial release
|
data/README
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
data/lib/spira.rb
CHANGED
@@ -85,6 +85,17 @@ module Spira
|
|
85
85
|
end
|
86
86
|
module_function :repository
|
87
87
|
|
88
|
+
##
|
89
|
+
# Clear all repositories from Spira's knowledge. Use it if you want, but
|
90
|
+
# it's really here for testing.
|
91
|
+
#
|
92
|
+
# @return [Void]
|
93
|
+
# @private
|
94
|
+
def clear_repositories!
|
95
|
+
settings[:repositories] = {}
|
96
|
+
end
|
97
|
+
module_function :clear_repositories!
|
98
|
+
|
88
99
|
##
|
89
100
|
# Alias a property type to another. This allows a range of options to be
|
90
101
|
# specified for a property type which all reference one Spira::Type
|
@@ -122,4 +133,20 @@ module RDF
|
|
122
133
|
klass.for(self, *args)
|
123
134
|
end
|
124
135
|
end
|
136
|
+
|
137
|
+
class Node
|
138
|
+
##
|
139
|
+
# Create a projection of this Node as the given Spira::Resource class.
|
140
|
+
# Equivalent to `klass.for(self, *args)`
|
141
|
+
#
|
142
|
+
# @example Instantiating a blank node as a Spira Resource
|
143
|
+
# RDF::Node.new.as(Person)
|
144
|
+
# @param [Class] klass
|
145
|
+
# @param [*Any] args Any arguments to pass to klass.for
|
146
|
+
# @return [Klass] An instance of klass
|
147
|
+
def as(klass, *args)
|
148
|
+
raise ArgumentError, "#{klass} is not a Spira resource" unless klass.is_a?(Class) && klass.ancestors.include?(Spira::Resource)
|
149
|
+
klass.for(self, *args)
|
150
|
+
end
|
151
|
+
end
|
125
152
|
end
|
data/lib/spira/exceptions.rb
CHANGED
@@ -7,5 +7,9 @@ module Spira
|
|
7
7
|
|
8
8
|
##
|
9
9
|
# For cases when a projection fails a validation check
|
10
|
-
class ValidationError < StandardError; end
|
10
|
+
class ValidationError < StandardError ; end
|
11
|
+
|
12
|
+
##
|
13
|
+
# For cases in which a repository is required but none has been given
|
14
|
+
class NoRepositoryError < StandardError ; end
|
11
15
|
end
|
@@ -10,19 +10,29 @@ module Spira
|
|
10
10
|
# @see Spira::Resource::DSL
|
11
11
|
module ClassMethods
|
12
12
|
|
13
|
+
##
|
14
|
+
# A symbol name for the repository this class is currently using.
|
15
|
+
attr_reader :repository_name
|
16
|
+
|
13
17
|
##
|
14
18
|
# The current repository for this class
|
15
19
|
#
|
16
|
-
# @
|
17
|
-
# @return [Void]
|
20
|
+
# @return [RDF::Repository, nil]
|
18
21
|
# @private
|
19
22
|
def repository
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
name = @repository_name || :default
|
24
|
+
Spira.repository(name)
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Get the current repository for this class, and raise a
|
29
|
+
# Spira::NoRepositoryError if it is nil.
|
30
|
+
#
|
31
|
+
# @raise [Spira::NoRepositoryError]
|
32
|
+
# @return [RDF::Repository]
|
33
|
+
# @private
|
34
|
+
def repository_or_fail
|
35
|
+
repository || (raise Spira::NoRepositoryError, "#{self} is configured to use #{@repository_name} as a repository, but it has not been set.")
|
26
36
|
end
|
27
37
|
|
28
38
|
##
|
@@ -57,26 +67,37 @@ module Spira
|
|
57
67
|
if !self.type.nil? && attributes[:type]
|
58
68
|
raise TypeError, "#{self} has an RDF type, #{self.type}, and cannot accept one as an argument."
|
59
69
|
end
|
60
|
-
uri =
|
70
|
+
uri = id_for(identifier)
|
61
71
|
self.new(uri, attributes)
|
62
72
|
end
|
63
73
|
|
64
74
|
##
|
65
|
-
# Creates a URI based on a base_uri and string
|
75
|
+
# Creates a URI or RDF::Node based on a potential base_uri and string,
|
76
|
+
# URI, or Node, or Addressable::URI. If not a URI or Node, the given
|
77
|
+
# identifier should be a string representing an absolute URI, or
|
78
|
+
# something responding to to_s which can be appended to a base URI, which
|
79
|
+
# this class must have.
|
66
80
|
#
|
67
81
|
# @param [Any] Identifier
|
68
|
-
# @return [RDF::URI]
|
69
|
-
# @raise [ArgumentError] If this class cannot create an identifier from the given
|
82
|
+
# @return [RDF::URI, RDF::Node]
|
83
|
+
# @raise [ArgumentError] If this class cannot create an identifier from the given argument
|
70
84
|
# @see http://rdf.rubyforge.org/RDF/URI.html
|
71
|
-
def
|
72
|
-
case
|
73
|
-
|
85
|
+
def id_for(identifier)
|
86
|
+
case
|
87
|
+
# Catches RDF::URI and implementing subclasses
|
88
|
+
when identifier.respond_to?(:to_uri)
|
89
|
+
identifier.to_uri
|
90
|
+
# Catches RDF::Nodes
|
91
|
+
when identifier.respond_to?(:node?) && identifier.node?
|
74
92
|
identifier
|
93
|
+
when identifier.is_a?(Addressable::URI)
|
94
|
+
RDF::URI.new(identifier)
|
95
|
+
# Treat identifier as a string, and create a URI out of it.
|
75
96
|
else
|
76
97
|
uri = RDF::URI.new(identifier.to_s)
|
77
98
|
return uri if uri.absolute?
|
78
99
|
raise ArgumentError, "Cannot create identifier for #{self} by String without base_uri; RDF::URI required" if self.base_uri.nil?
|
79
|
-
separator = self.base_uri.to_s[-1,1]
|
100
|
+
separator = self.base_uri.to_s[-1,1] =~ /(\/|#)/ ? '' : '/'
|
80
101
|
RDF::URI.new(self.base_uri.to_s + separator + identifier.to_s)
|
81
102
|
end
|
82
103
|
end
|
data/lib/spira/resource/dsl.rb
CHANGED
@@ -132,7 +132,7 @@ module Spira
|
|
132
132
|
# Build a Ruby value from an RDF value.
|
133
133
|
#
|
134
134
|
# @private
|
135
|
-
def build_value(statement, type
|
135
|
+
def build_value(statement, type)
|
136
136
|
case
|
137
137
|
when statement == nil
|
138
138
|
nil
|
@@ -146,15 +146,9 @@ module Spira
|
|
146
146
|
raise TypeError, "#{type} is not a Spira Resource (referenced as #{type} by #{self}"
|
147
147
|
end
|
148
148
|
end
|
149
|
-
|
150
|
-
when false && existing_relation && (existing_relation.uri == statement.object.to_uri)
|
151
|
-
existing_relation
|
152
|
-
else
|
153
|
-
promise { klass.for(statement.object) ||
|
154
|
-
klass.create(statement.object) }
|
155
|
-
end
|
149
|
+
promise { klass.for(statement.object) }
|
156
150
|
else
|
157
|
-
raise TypeError, "Unable to unserialize #{statement.object}
|
151
|
+
raise TypeError, "Unable to unserialize #{statement.object} as #{type}"
|
158
152
|
end
|
159
153
|
end
|
160
154
|
|
@@ -165,11 +159,9 @@ module Spira
|
|
165
159
|
when type.is_a?(Class) && type.ancestors.include?(Spira::Type)
|
166
160
|
type.serialize(value)
|
167
161
|
when value && value.class.ancestors.include?(Spira::Resource)
|
168
|
-
value.
|
169
|
-
when type == RDF::URI && value.is_a?(RDF::URI)
|
170
|
-
value
|
162
|
+
value.subject
|
171
163
|
else
|
172
|
-
raise TypeError, "Unable to serialize #{value}
|
164
|
+
raise TypeError, "Unable to serialize #{value} as #{type}"
|
173
165
|
end
|
174
166
|
end
|
175
167
|
|
@@ -17,18 +17,18 @@ module Spira
|
|
17
17
|
# This instance's URI.
|
18
18
|
#
|
19
19
|
# @return [RDF::URI]
|
20
|
-
attr_reader :
|
20
|
+
attr_reader :subject
|
21
21
|
|
22
22
|
##
|
23
23
|
# Initialize a new Spira::Resource instance of this resource class. This
|
24
24
|
# method should not be called directly, use
|
25
25
|
# {Spira::Resource::ClassMethods#for} instead.
|
26
26
|
#
|
27
|
-
# @param [
|
27
|
+
# @param [RDF::URI, RDF::Node] identifier The URI or URI fragment for this instance
|
28
28
|
# @param [Hash] opts Default attributes for this instance
|
29
29
|
# @see Spira::Resource::ClassMethods#for
|
30
30
|
def initialize(identifier, opts = {})
|
31
|
-
@
|
31
|
+
@subject = identifier
|
32
32
|
reload(opts)
|
33
33
|
end
|
34
34
|
|
@@ -54,10 +54,7 @@ module Spira
|
|
54
54
|
# @return [Hash] @attributes
|
55
55
|
# @private
|
56
56
|
def reload_attributes()
|
57
|
-
|
58
|
-
raise RuntimeError, "#{self} is configured to use #{@repository_name} as a repository, but was unable to find it."
|
59
|
-
end
|
60
|
-
statements = self.class.repository.query(:subject => @uri)
|
57
|
+
statements = self.class.repository_or_fail.query(:subject => @subject)
|
61
58
|
@attributes = {}
|
62
59
|
|
63
60
|
unless statements.empty?
|
@@ -70,7 +67,7 @@ module Spira
|
|
70
67
|
# execute the promises to load those classes. Need an identity
|
71
68
|
# map of some sort to fix that.
|
72
69
|
values = []
|
73
|
-
collection = statements.query(:subject => @
|
70
|
+
collection = statements.query(:subject => @subject, :predicate => property[:predicate])
|
74
71
|
unless collection.nil?
|
75
72
|
collection.each do |statement|
|
76
73
|
values << self.class.build_value(statement,property[:type])
|
@@ -78,7 +75,7 @@ module Spira
|
|
78
75
|
end
|
79
76
|
attribute_set(name, values)
|
80
77
|
else
|
81
|
-
statement = statements.query(:subject => @
|
78
|
+
statement = statements.query(:subject => @subject, :predicate => property[:predicate]).first
|
82
79
|
attribute_set(name, self.class.build_value(statement, property[:type]))
|
83
80
|
end
|
84
81
|
end
|
@@ -105,8 +102,8 @@ module Spira
|
|
105
102
|
# @private
|
106
103
|
def _destroy_attributes(attributes, opts = {})
|
107
104
|
repository = repository_for_attributes(attributes)
|
108
|
-
repository.insert([@
|
109
|
-
self.class.
|
105
|
+
repository.insert([@subject, RDF.type, self.class.type]) if (self.class.type && opts[:destroy_type])
|
106
|
+
self.class.repository_or_fail.delete(*repository)
|
110
107
|
end
|
111
108
|
|
112
109
|
##
|
@@ -126,7 +123,7 @@ module Spira
|
|
126
123
|
#
|
127
124
|
# @return [true, false] Whether or not the destroy was successful
|
128
125
|
def destroy_resource!
|
129
|
-
self.class.
|
126
|
+
self.class.repository_or_fail.delete([@subject,nil,nil])
|
130
127
|
end
|
131
128
|
|
132
129
|
##
|
@@ -134,9 +131,6 @@ module Spira
|
|
134
131
|
#
|
135
132
|
# @return [true, false] Whether or not the save was successful
|
136
133
|
def save!
|
137
|
-
if self.class.repository.nil?
|
138
|
-
raise RuntimeError, "#{self} is configured to use #{@repository_name} as a repository, but was unable to find it."
|
139
|
-
end
|
140
134
|
unless self.class.validators.empty?
|
141
135
|
errors.clear
|
142
136
|
self.class.validators.each do | validator | self.send(validator) end
|
@@ -156,7 +150,7 @@ module Spira
|
|
156
150
|
# @private
|
157
151
|
def _update!
|
158
152
|
_destroy_attributes(@original_attributes)
|
159
|
-
self.class.
|
153
|
+
self.class.repository_or_fail.insert(*self)
|
160
154
|
@original_attributes = @attributes.dup
|
161
155
|
end
|
162
156
|
|
@@ -185,20 +179,12 @@ module Spira
|
|
185
179
|
self
|
186
180
|
end
|
187
181
|
|
188
|
-
##
|
189
|
-
# Returns the URI representation of this resource.
|
190
|
-
#
|
191
|
-
# @return [RDF::URI]
|
192
|
-
def to_uri
|
193
|
-
uri
|
194
|
-
end
|
195
|
-
|
196
182
|
##
|
197
183
|
# A developer-friendly view of this projection
|
198
184
|
#
|
199
185
|
# @private
|
200
186
|
def inspect
|
201
|
-
"<#{self.class}:#{self.object_id} uri: #{@
|
187
|
+
"<#{self.class}:#{self.object_id} uri: #{@subject}>"
|
202
188
|
end
|
203
189
|
|
204
190
|
##
|
@@ -211,13 +197,12 @@ module Spira
|
|
211
197
|
def each(*args, &block)
|
212
198
|
return RDF::Enumerator.new(self, :each) unless block_given?
|
213
199
|
repository = repository_for_attributes(@attributes)
|
214
|
-
repository.insert(RDF::Statement.new(@
|
200
|
+
repository.insert(RDF::Statement.new(@subject, RDF.type, type)) unless type.nil?
|
215
201
|
repository.each(*args, &block)
|
216
202
|
end
|
217
203
|
|
218
204
|
##
|
219
|
-
#
|
220
|
-
# private.
|
205
|
+
# Sets the given attribute to the given value.
|
221
206
|
#
|
222
207
|
# @private
|
223
208
|
def attribute_set(name, value)
|
@@ -225,7 +210,7 @@ module Spira
|
|
225
210
|
end
|
226
211
|
|
227
212
|
##
|
228
|
-
#
|
213
|
+
# Get the current value for the given attribute
|
229
214
|
#
|
230
215
|
# @private
|
231
216
|
def attribute_get(name)
|
@@ -251,12 +236,12 @@ module Spira
|
|
251
236
|
new = []
|
252
237
|
attribute.each do |value|
|
253
238
|
value = self.class.build_rdf_value(value, self.class.properties[name][:type])
|
254
|
-
new << RDF::Statement.new(@
|
239
|
+
new << RDF::Statement.new(@subject, self.class.properties[name][:predicate], value)
|
255
240
|
end
|
256
241
|
repo.insert(*new)
|
257
242
|
else
|
258
243
|
value = self.class.build_rdf_value(attribute, self.class.properties[name][:type])
|
259
|
-
repo.insert(RDF::Statement.new(@
|
244
|
+
repo.insert(RDF::Statement.new(@subject, self.class.properties[name][:predicate], value))
|
260
245
|
end
|
261
246
|
end
|
262
247
|
repo
|
@@ -273,7 +258,7 @@ module Spira
|
|
273
258
|
# TODO: define behavior for equality on subclasses.
|
274
259
|
# TODO: should we compare attributes here?
|
275
260
|
when self.class
|
276
|
-
@
|
261
|
+
@subject == other.uri
|
277
262
|
when RDF::Enumerable
|
278
263
|
self.isomorphic_with?(other)
|
279
264
|
else
|
@@ -281,6 +266,60 @@ module Spira
|
|
281
266
|
end
|
282
267
|
end
|
283
268
|
|
269
|
+
##
|
270
|
+
# Returns true for :to_uri if this instance's subject is a URI, and false if it is not.
|
271
|
+
# Returns true for :to_node if this instance's subject is a Node, and false if it is not.
|
272
|
+
# Calls super otherwise.
|
273
|
+
#
|
274
|
+
# @private
|
275
|
+
def respond_to?(*args)
|
276
|
+
case args[0]
|
277
|
+
when :to_uri
|
278
|
+
@subject.respond_to?(:to_uri)
|
279
|
+
when :to_node
|
280
|
+
@subject.node?
|
281
|
+
else
|
282
|
+
super(*args)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
##
|
287
|
+
# Returns the RDF::URI associated with this instance if this instance's
|
288
|
+
# subject is an RDF::URI, and nil otherwise.
|
289
|
+
#
|
290
|
+
# @return [RDF::URI,nil]
|
291
|
+
def uri
|
292
|
+
@subject.respond_to?(:to_uri) ? @subject : nil
|
293
|
+
end
|
294
|
+
|
295
|
+
##
|
296
|
+
# Returns the URI representation of this resource, if available. If this
|
297
|
+
# resource's subject is a BNode, raises a NoMethodError.
|
298
|
+
#
|
299
|
+
# @return [RDF::URI]
|
300
|
+
# @raise [NoMethodError]
|
301
|
+
def to_uri
|
302
|
+
uri || (raise NoMethodError, "No such method: :to_uri (this instance's subject is not a URI")
|
303
|
+
end
|
304
|
+
|
305
|
+
##
|
306
|
+
# Returns true if the subject associated with this instance is a blank node.
|
307
|
+
#
|
308
|
+
# @return [true, false]
|
309
|
+
def node?
|
310
|
+
@subject.node?
|
311
|
+
end
|
312
|
+
|
313
|
+
##
|
314
|
+
# Returns the Node subject of this resource, if available. If this
|
315
|
+
# resource's subject is a URI, raises a NoMethodError.
|
316
|
+
#
|
317
|
+
# @return [RDF::Node]
|
318
|
+
# @raise [NoMethodError]
|
319
|
+
def to_node
|
320
|
+
@subject.node? ? @subject : (raise NoMethodError, "No such method: :to_uri (this instance's subject is not a URI")
|
321
|
+
end
|
322
|
+
|
284
323
|
##
|
285
324
|
# The validation errors collection associated with this instance.
|
286
325
|
#
|
data/lib/spira/version.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Ben Lavender
|
@@ -15,7 +15,7 @@ bindir:
|
|
15
15
|
- bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-06-
|
18
|
+
date: 2010-06-07 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -28,8 +28,8 @@ dependencies:
|
|
28
28
|
segments:
|
29
29
|
- 0
|
30
30
|
- 1
|
31
|
-
-
|
32
|
-
version: 0.1.
|
31
|
+
- 10
|
32
|
+
version: 0.1.10
|
33
33
|
type: :development
|
34
34
|
version_requirements: *id001
|
35
35
|
- !ruby/object:Gem::Dependency
|
@@ -70,8 +70,8 @@ dependencies:
|
|
70
70
|
segments:
|
71
71
|
- 0
|
72
72
|
- 1
|
73
|
-
-
|
74
|
-
version: 0.1.
|
73
|
+
- 10
|
74
|
+
version: 0.1.10
|
75
75
|
type: :runtime
|
76
76
|
version_requirements: *id004
|
77
77
|
- !ruby/object:Gem::Dependency
|
@@ -98,8 +98,8 @@ dependencies:
|
|
98
98
|
segments:
|
99
99
|
- 0
|
100
100
|
- 1
|
101
|
-
-
|
102
|
-
version: 0.1.
|
101
|
+
- 1
|
102
|
+
version: 0.1.1
|
103
103
|
type: :runtime
|
104
104
|
version_requirements: *id006
|
105
105
|
description: Spira is a framework for using the information in RDF.rb repositories as model objects.
|
@@ -111,6 +111,7 @@ extensions: []
|
|
111
111
|
extra_rdoc_files: []
|
112
112
|
|
113
113
|
files:
|
114
|
+
- CHANGES.md
|
114
115
|
- AUTHORS
|
115
116
|
- README
|
116
117
|
- UNLICENSE
|