spira 0.0.12 → 0.5.0
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/AUTHORS +2 -0
- data/CHANGES.md +6 -3
- data/{README → README.md} +91 -120
- data/lib/rdf/ext/uri.rb +37 -0
- data/lib/spira.rb +47 -86
- data/lib/spira/association_reflection.rb +25 -0
- data/lib/spira/base.rb +353 -4
- data/lib/spira/exceptions.rb +13 -7
- data/lib/spira/persistence.rb +531 -0
- data/lib/spira/reflections.rb +19 -0
- data/lib/spira/resource.rb +165 -60
- data/lib/spira/serialization.rb +27 -0
- data/lib/spira/type.rb +7 -7
- data/lib/spira/types.rb +8 -11
- data/lib/spira/types/boolean.rb +3 -3
- data/lib/spira/types/date.rb +4 -5
- data/lib/spira/types/dateTime.rb +26 -0
- data/lib/spira/types/decimal.rb +2 -2
- data/lib/spira/types/float.rb +3 -3
- data/lib/spira/types/gYear.rb +26 -0
- data/lib/spira/types/int.rb +27 -0
- data/lib/spira/types/integer.rb +3 -3
- data/lib/spira/types/long.rb +27 -0
- data/lib/spira/types/negativeInteger.rb +27 -0
- data/lib/spira/types/nonNegativeInteger.rb +27 -0
- data/lib/spira/types/nonPositiveInteger.rb +27 -0
- data/lib/spira/types/positiveInteger.rb +27 -0
- data/lib/spira/types/string.rb +1 -1
- data/lib/spira/utils.rb +29 -0
- data/lib/spira/validations.rb +77 -0
- data/lib/spira/validations/uniqueness.rb +33 -0
- data/lib/spira/version.rb +3 -6
- metadata +218 -123
- data/lib/spira/errors.rb +0 -94
- data/lib/spira/extensions.rb +0 -14
- data/lib/spira/resource/class_methods.rb +0 -260
- data/lib/spira/resource/dsl.rb +0 -279
- data/lib/spira/resource/instance_methods.rb +0 -567
- data/lib/spira/resource/validations.rb +0 -47
data/lib/spira/errors.rb
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
module Spira
|
2
|
-
|
3
|
-
##
|
4
|
-
# Spira::Errors represents a collection of validation errors for a Spira
|
5
|
-
# resource. It tracks a list of errors for each field.
|
6
|
-
#
|
7
|
-
# This class does not perform validations. It only tracks the results of
|
8
|
-
# them.
|
9
|
-
class Errors
|
10
|
-
|
11
|
-
##
|
12
|
-
# Creates a new Spira::Errors
|
13
|
-
#
|
14
|
-
# @return [Spira::Errors]
|
15
|
-
def initialize
|
16
|
-
@errors = {}
|
17
|
-
end
|
18
|
-
|
19
|
-
##
|
20
|
-
# Returns true if there are no errors, false otherwise
|
21
|
-
#
|
22
|
-
# @return [true, false]
|
23
|
-
def empty?
|
24
|
-
@errors.all? do |field, errors| errors.empty? end
|
25
|
-
end
|
26
|
-
|
27
|
-
##
|
28
|
-
# Returns true if there are errors, false otherwise
|
29
|
-
#
|
30
|
-
# @return [true, false]
|
31
|
-
def any?
|
32
|
-
!empty?
|
33
|
-
end
|
34
|
-
|
35
|
-
##
|
36
|
-
# Returns true if the given property or list has any errors
|
37
|
-
#
|
38
|
-
# @param [Symbol] name The name of the property or list
|
39
|
-
# @return [true, false]
|
40
|
-
def any_for?(property)
|
41
|
-
!(@errors[property].nil?) && !(@errors[property].empty?)
|
42
|
-
end
|
43
|
-
|
44
|
-
##
|
45
|
-
# Add an error to a given property or list
|
46
|
-
#
|
47
|
-
# @example Add an error to a property
|
48
|
-
# errors.add(:username, "cannot be nil")
|
49
|
-
# @param [Symbol] property The property or list to add the error to
|
50
|
-
# @param [String] problem The error
|
51
|
-
# @return [Void]
|
52
|
-
def add(property, problem)
|
53
|
-
@errors[property] ||= []
|
54
|
-
@errors[property].push problem
|
55
|
-
end
|
56
|
-
|
57
|
-
##
|
58
|
-
# The list of errors for a given property or list
|
59
|
-
#
|
60
|
-
# @example Get the errors for the `:username` field
|
61
|
-
# errors.add(:username, "cannot be nil")
|
62
|
-
# errors.for(:username) #=> ["cannot be nil"]
|
63
|
-
# @param [Symbol] property The property or list to check
|
64
|
-
# @return [Array<String>] The list of errors
|
65
|
-
def for(property)
|
66
|
-
@errors[property]
|
67
|
-
end
|
68
|
-
|
69
|
-
##
|
70
|
-
# Clear all errors
|
71
|
-
#
|
72
|
-
# @return [Void]
|
73
|
-
def clear
|
74
|
-
@errors = {}
|
75
|
-
end
|
76
|
-
|
77
|
-
##
|
78
|
-
# Return all errors as strings
|
79
|
-
#
|
80
|
-
# @example Get all errors
|
81
|
-
# errors.add(:username, "cannot be nil")
|
82
|
-
# errors.each #=> ["username cannot be nil"]
|
83
|
-
# @return [Array<String>]
|
84
|
-
def each
|
85
|
-
@errors.map do |property, problems|
|
86
|
-
problems.map do |problem|
|
87
|
-
property.to_s + " " + problem
|
88
|
-
end
|
89
|
-
end.flatten
|
90
|
-
end
|
91
|
-
|
92
|
-
|
93
|
-
end
|
94
|
-
end
|
data/lib/spira/extensions.rb
DELETED
@@ -1,260 +0,0 @@
|
|
1
|
-
module Spira
|
2
|
-
module Resource
|
3
|
-
|
4
|
-
##
|
5
|
-
# This module contains all class methods available to a declared Spira::Resource class.
|
6
|
-
# {Spira::Resource} contains more information about Spira resources.
|
7
|
-
#
|
8
|
-
# @see Spira::Resource
|
9
|
-
# @see Spira::Resource::InstanceMethods
|
10
|
-
# @see Spira::Resource::DSL
|
11
|
-
module ClassMethods
|
12
|
-
|
13
|
-
##
|
14
|
-
# A symbol name for the repository this class is currently using.
|
15
|
-
attr_reader :repository_name
|
16
|
-
|
17
|
-
##
|
18
|
-
# The current repository for this class
|
19
|
-
#
|
20
|
-
# @return [RDF::Repository, nil]
|
21
|
-
# @private
|
22
|
-
def repository
|
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 || 'default'} as a repository, but it has not been set.")
|
36
|
-
end
|
37
|
-
|
38
|
-
##
|
39
|
-
# Create a new projection instance of this class for the given URI. If a
|
40
|
-
# class has a base_uri given, and the argument is not an `RDF::URI`, the
|
41
|
-
# given identifier will be appended to the base URI.
|
42
|
-
#
|
43
|
-
# Spira does not have 'find' or 'create' functions. As RDF identifiers
|
44
|
-
# are globally unique, they all simply 'are'.
|
45
|
-
#
|
46
|
-
# On calling `for`, a new projection is created for the given URI. The
|
47
|
-
# first time access is attempted on a field, the repository will be
|
48
|
-
# queried for existing attributes, which will be used for the given URI.
|
49
|
-
# Underlying repositories are not accessed at the time of calling `for`.
|
50
|
-
#
|
51
|
-
# A class with a base URI may still be projected for any URI, whether or
|
52
|
-
# not it uses the given resource class' base URI.
|
53
|
-
#
|
54
|
-
# @raise [TypeError] if an RDF type is given in the attributes and one is
|
55
|
-
# given in the attributes.
|
56
|
-
# @raise [ArgumentError] if a non-URI is given and the class does not
|
57
|
-
# have a base URI.
|
58
|
-
# @overload for(uri, attributes = {})
|
59
|
-
# @param [RDF::URI] uri The URI to create an instance for
|
60
|
-
# @param [Hash{Symbol => Any}] attributes Initial attributes
|
61
|
-
# @overload for(identifier, attributes = {})
|
62
|
-
# @param [Any] uri The identifier to append to the base URI for this class
|
63
|
-
# @param [Hash{Symbol => Any}] attributes Initial attributes
|
64
|
-
# @yield [self] Executes a given block and calls `#save!`
|
65
|
-
# @yieldparam [self] self The newly created instance
|
66
|
-
# @return [Spira::Resource] The newly created instance
|
67
|
-
# @see http://rdf.rubyforge.org/RDF/URI.html
|
68
|
-
def for(identifier, attributes = {}, &block)
|
69
|
-
self.project(id_for(identifier), attributes, &block)
|
70
|
-
end
|
71
|
-
|
72
|
-
##
|
73
|
-
# Create a new instance with the given subjet without any modification to
|
74
|
-
# the given subject at all. This method exists to provide an entry point
|
75
|
-
# for implementing classes that want to create a more intelligent .for
|
76
|
-
# and/or .id_for for their given use cases, such as simple string
|
77
|
-
# appending to base URIs or calculated URIs from other representations.
|
78
|
-
#
|
79
|
-
# @example Using simple string concatentation with base_uri in .for instead of joining delimiters
|
80
|
-
# def for(identifier, attributes = {}, &block)
|
81
|
-
# self.project(RDF::URI(self.base_uri.to_s + identifier.to_s), attributes, &block)
|
82
|
-
# end
|
83
|
-
# @param [RDF::URI, RDF::Node] subject
|
84
|
-
# @param [Hash{Symbol => Any}] attributes Initial attributes
|
85
|
-
# @return [Spira::Resource] the newly created instance
|
86
|
-
def project(subject, attributes = {}, &block)
|
87
|
-
if !self.type.nil? && attributes[:type]
|
88
|
-
raise TypeError, "#{self} has an RDF type, #{self.type}, and cannot accept one as an argument."
|
89
|
-
end
|
90
|
-
self.new(attributes.merge(:_subject => subject), &block)
|
91
|
-
end
|
92
|
-
|
93
|
-
##
|
94
|
-
# Alias for #for
|
95
|
-
#
|
96
|
-
# @see #for
|
97
|
-
def [](*args)
|
98
|
-
self.for(*args)
|
99
|
-
end
|
100
|
-
|
101
|
-
##
|
102
|
-
# Creates a URI or RDF::Node based on a potential base_uri and string,
|
103
|
-
# URI, or Node, or Addressable::URI. If not a URI or Node, the given
|
104
|
-
# identifier should be a string representing an absolute URI, or
|
105
|
-
# something responding to to_s which can be appended to a base URI, which
|
106
|
-
# this class must have.
|
107
|
-
#
|
108
|
-
# @param [Any] Identifier
|
109
|
-
# @return [RDF::URI, RDF::Node]
|
110
|
-
# @raise [ArgumentError] If this class cannot create an identifier from the given argument
|
111
|
-
# @see http://rdf.rubyforge.org/RDF/URI.html
|
112
|
-
# @see Spira::Resource.base_uri
|
113
|
-
# @see Spira::Resource.for
|
114
|
-
def id_for(identifier)
|
115
|
-
case
|
116
|
-
# Absolute URI's go through unchanged
|
117
|
-
when identifier.is_a?(RDF::URI) && identifier.absolute?
|
118
|
-
identifier
|
119
|
-
# We don't have a base URI to join this fragment with, so go ahead and instantiate it as-is.
|
120
|
-
when identifier.is_a?(RDF::URI) && self.base_uri.nil?
|
121
|
-
identifier
|
122
|
-
# Blank nodes go through unchanged
|
123
|
-
when identifier.respond_to?(:node?) && identifier.node?
|
124
|
-
identifier
|
125
|
-
# Anything that can be an RDF::URI, we re-run this case statement
|
126
|
-
# on it for the fragment logic above.
|
127
|
-
when identifier.respond_to?(:to_uri) && !identifier.is_a?(RDF::URI)
|
128
|
-
id_for(identifier.to_uri)
|
129
|
-
# see comment with #to_uri above, this might be a fragment
|
130
|
-
when identifier.is_a?(Addressable::URI)
|
131
|
-
id_for(RDF::URI.intern(identifier))
|
132
|
-
# This is a #to_s or a URI fragment with a base uri. We'll treat them the same.
|
133
|
-
# FIXME: when #/ makes it into RDF.rb proper, this can all be wrapped
|
134
|
-
# into the one case statement above.
|
135
|
-
else
|
136
|
-
uri = identifier.is_a?(RDF::URI) ? identifier : RDF::URI.intern(identifier.to_s)
|
137
|
-
case
|
138
|
-
when uri.absolute?
|
139
|
-
uri
|
140
|
-
when self.base_uri.nil?
|
141
|
-
raise ArgumentError, "Cannot create identifier for #{self} by String without base_uri; an RDF::URI is required" if self.base_uri.nil?
|
142
|
-
else
|
143
|
-
separator = self.base_uri.to_s[-1,1] =~ /(\/|#)/ ? '' : '/'
|
144
|
-
RDF::URI.intern(self.base_uri.to_s + separator + identifier.to_s)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
|
150
|
-
##
|
151
|
-
# The number of URIs projectable as a given class in the repository.
|
152
|
-
# This method is only valid for classes which declare a `type` with the
|
153
|
-
# `type` method in the DSL.
|
154
|
-
#
|
155
|
-
# @raise [Spira::NoTypeError] if the resource class does not have an RDF type declared
|
156
|
-
# @return [Integer] the count
|
157
|
-
# @see Spira::Resource::DSL
|
158
|
-
def count
|
159
|
-
raise Spira::NoTypeError, "Cannot count a #{self} without a reference type URI." if @type.nil?
|
160
|
-
repository.query(:predicate => RDF.type, :object => @type).subjects.count
|
161
|
-
end
|
162
|
-
|
163
|
-
##
|
164
|
-
# A cache of iterated instances of this projection
|
165
|
-
#
|
166
|
-
# @return [RDF::Util::Cache]
|
167
|
-
# @private
|
168
|
-
def cache
|
169
|
-
@cache ||= RDF::Util::Cache.new
|
170
|
-
end
|
171
|
-
|
172
|
-
##
|
173
|
-
# Clear the iteration cache
|
174
|
-
#
|
175
|
-
# @return [void]
|
176
|
-
def reload
|
177
|
-
@cache = nil
|
178
|
-
end
|
179
|
-
|
180
|
-
##
|
181
|
-
# Enumerate over all resources projectable as this class. This method is
|
182
|
-
# only valid for classes which declare a `type` with the `type` method in
|
183
|
-
# the DSL.
|
184
|
-
#
|
185
|
-
# @raise [Spira::NoTypeError] if the resource class does not have an RDF type declared
|
186
|
-
# @overload each
|
187
|
-
# @yield [instance] A block to perform for each available projection of this class
|
188
|
-
# @yieldparam [self] instance
|
189
|
-
# @yieldreturn [Void]
|
190
|
-
# @return [Void]
|
191
|
-
#
|
192
|
-
# @overload each
|
193
|
-
# @return [Enumerator]
|
194
|
-
# @see Spira::Resource::DSL
|
195
|
-
def each(&block)
|
196
|
-
raise Spira::NoTypeError, "Cannot count a #{self} without a reference type URI." if @type.nil?
|
197
|
-
case block_given?
|
198
|
-
when false
|
199
|
-
enum_for(:each)
|
200
|
-
else
|
201
|
-
repository_or_fail.query(:predicate => RDF.type, :object => @type).each_subject do |subject|
|
202
|
-
self.cache[subject] ||= self.for(subject)
|
203
|
-
block.call(cache[subject])
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
##
|
209
|
-
# Returns true if the given property is a has_many property, false otherwise
|
210
|
-
#
|
211
|
-
# @return [true, false]
|
212
|
-
def is_list?(property)
|
213
|
-
@lists.has_key?(property)
|
214
|
-
end
|
215
|
-
|
216
|
-
##
|
217
|
-
# Handling inheritance
|
218
|
-
#
|
219
|
-
# @private
|
220
|
-
def inherited(child)
|
221
|
-
child.instance_eval do
|
222
|
-
include Spira::Resource
|
223
|
-
end
|
224
|
-
# FIXME: This is clearly brittle and ugly.
|
225
|
-
[:@base_uri, :@default_vocabulary, :@repository_name, :@type].each do |variable|
|
226
|
-
value = instance_variable_get(variable).nil? ? nil : instance_variable_get(variable).dup
|
227
|
-
child.instance_variable_set(variable, value)
|
228
|
-
end
|
229
|
-
[:@properties, :@lists, :@validators].each do |variable|
|
230
|
-
if child.instance_variable_get(variable).nil?
|
231
|
-
if instance_variable_get(variable).nil?
|
232
|
-
child.instance_variable_set(variable, nil)
|
233
|
-
else
|
234
|
-
child.instance_variable_set(variable, instance_variable_get(variable).dup)
|
235
|
-
end
|
236
|
-
elsif !(instance_variable_get(variable).nil?)
|
237
|
-
child.instance_variable_set(variable, instance_variable_get(variable).dup.merge(child.instance_variable_get(variable)))
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
##
|
243
|
-
# Handling module inclusions
|
244
|
-
#
|
245
|
-
# @private
|
246
|
-
def included(child)
|
247
|
-
inherited(child)
|
248
|
-
end
|
249
|
-
|
250
|
-
##
|
251
|
-
# The list of validation functions for this projection
|
252
|
-
#
|
253
|
-
# @return [Array<Symbol>]
|
254
|
-
def validators
|
255
|
-
@validators ||= []
|
256
|
-
end
|
257
|
-
|
258
|
-
end
|
259
|
-
end
|
260
|
-
end
|
data/lib/spira/resource/dsl.rb
DELETED
@@ -1,279 +0,0 @@
|
|
1
|
-
module Spira
|
2
|
-
module Resource
|
3
|
-
|
4
|
-
##
|
5
|
-
# This module consists of Spira::Resource class methods which correspond to
|
6
|
-
# the Spira resource class declaration DSL. See {Spira::Resource} for more
|
7
|
-
# information.
|
8
|
-
#
|
9
|
-
# @see Spira::Resource
|
10
|
-
# @see Spira::Resource::ClassMethods
|
11
|
-
# @see Spira::Resource::InstanceMethods
|
12
|
-
# @see Spira::Resource::Validations
|
13
|
-
module DSL
|
14
|
-
|
15
|
-
##
|
16
|
-
# The name of the default repository to use for this class. This
|
17
|
-
# repository will be queried and written to instead of the :default
|
18
|
-
# repository.
|
19
|
-
#
|
20
|
-
# @param [Symbol] name
|
21
|
-
# @return [Void]
|
22
|
-
def default_source(name)
|
23
|
-
@repository_name = name
|
24
|
-
@repository = Spira.repository(name)
|
25
|
-
end
|
26
|
-
|
27
|
-
##
|
28
|
-
# The base URI for this class. Attempts to create instances for non-URI
|
29
|
-
# objects will be appended to this base URI.
|
30
|
-
#
|
31
|
-
# @param [String, RDF::URI] base uri
|
32
|
-
# @return [Void]
|
33
|
-
def base_uri(uri = nil)
|
34
|
-
@base_uri = uri unless uri.nil?
|
35
|
-
@base_uri
|
36
|
-
end
|
37
|
-
|
38
|
-
##
|
39
|
-
# The default vocabulary for this class. Setting a default vocabulary
|
40
|
-
# will allow properties to be defined without a `:predicate` option.
|
41
|
-
# Predicates will instead be created by appending the property name to
|
42
|
-
# the given string.
|
43
|
-
#
|
44
|
-
# @param [String, RDF::URI] base uri
|
45
|
-
# @return [Void]
|
46
|
-
def default_vocabulary(uri)
|
47
|
-
@default_vocabulary = uri
|
48
|
-
end
|
49
|
-
|
50
|
-
|
51
|
-
##
|
52
|
-
# Add a property to this class. A property is an accessor field that
|
53
|
-
# represents an RDF predicate.
|
54
|
-
#
|
55
|
-
# @example A simple string property
|
56
|
-
# property :name, :predicate => FOAF.name, :type => String
|
57
|
-
# @example A property which defaults to {Spira::Types::Any}
|
58
|
-
# property :name, :predicate => FOAF.name
|
59
|
-
# @example An integer property
|
60
|
-
# property :age, :predicate => FOAF.age, :type => Integer
|
61
|
-
# @param [Symbol] name The name of this property
|
62
|
-
# @param [Hash{Symbol => Any}] opts property options
|
63
|
-
# @option opts [RDF::URI] :predicate The RDF predicate which will refer to this property
|
64
|
-
# @option opts [Spira::Type, String] :type (Spira::Types::Any) The
|
65
|
-
# type for this property. If a Spira::Type is given, that class will be
|
66
|
-
# used to serialize and unserialize values. If a String is given, it
|
67
|
-
# should be the String form of a Spira::Resource class name (Strings are
|
68
|
-
# used to prevent issues with load order).
|
69
|
-
# @see Spira::Types
|
70
|
-
# @see Spira::Type
|
71
|
-
# @return [Void]
|
72
|
-
def property(name, opts = {} )
|
73
|
-
predicate = predicate_for(opts[:predicate], name)
|
74
|
-
type = type_for(opts[:type])
|
75
|
-
@properties[name] = { :predicate => predicate, :type => type }
|
76
|
-
add_accessors(name,opts)
|
77
|
-
end
|
78
|
-
|
79
|
-
##
|
80
|
-
# The plural form of `property`. `Has_many` has the same options as
|
81
|
-
# `property`, but instead of a single value, a Ruby Array of objects will
|
82
|
-
# be created instead.
|
83
|
-
#
|
84
|
-
# has_many corresponds to an RDF subject with several triples of the same
|
85
|
-
# predicate. This corresponds to a Ruby Set, which will be returned when
|
86
|
-
# the property is accessed. Arrays will be accepted for new values, but
|
87
|
-
# ordering and duplicate values will be lost on save.
|
88
|
-
#
|
89
|
-
# @see Spira::Resource::DSL#property
|
90
|
-
def has_many(name, opts = {})
|
91
|
-
property(name, opts)
|
92
|
-
@lists[name] = true
|
93
|
-
end
|
94
|
-
|
95
|
-
##
|
96
|
-
# Validate this model with the given validator function name.
|
97
|
-
#
|
98
|
-
# @example
|
99
|
-
# class Person
|
100
|
-
# include Spira::Resource
|
101
|
-
# property :name, :predicate => FOAF.name
|
102
|
-
# validate :is_awesome
|
103
|
-
# def is_awesome
|
104
|
-
# assert(name =~ /Thor/, :name, "not awesome")
|
105
|
-
# end
|
106
|
-
# end
|
107
|
-
# @param [Symbol] validator
|
108
|
-
# @return [Void]
|
109
|
-
def validate(validator)
|
110
|
-
validators << validator unless validators.include?(validator)
|
111
|
-
end
|
112
|
-
|
113
|
-
|
114
|
-
##
|
115
|
-
# Associate an RDF type with this class. RDF resources can be multiple
|
116
|
-
# types at once, but if they have an `RDF.type` statement for the given
|
117
|
-
# URI, this class can #count them.
|
118
|
-
#
|
119
|
-
# @param [RDF::URI] uri The URI object of the `RDF.type` triple
|
120
|
-
# @return [Void]
|
121
|
-
# @see http://rdf.rubyforge.net/RDF/URI.html
|
122
|
-
# @see http://rdf.rubyforge.org/RDF.html#type-class_method
|
123
|
-
# @see Spira::Resource::ClassMethods#count
|
124
|
-
def type(uri = nil)
|
125
|
-
unless uri.nil?
|
126
|
-
@type = case uri
|
127
|
-
when RDF::URI
|
128
|
-
uri
|
129
|
-
else
|
130
|
-
raise TypeError, "Cannot assign type #{uri} (of type #{uri.class}) to #{self}, expected RDF::URI"
|
131
|
-
end
|
132
|
-
end
|
133
|
-
@type
|
134
|
-
end
|
135
|
-
|
136
|
-
# Build a Ruby value from an RDF value.
|
137
|
-
#
|
138
|
-
# @private
|
139
|
-
def build_value(statement, type, cache)
|
140
|
-
case
|
141
|
-
when statement == nil
|
142
|
-
nil
|
143
|
-
when !cache[statement.object].nil?
|
144
|
-
cache[statement.object]
|
145
|
-
when type.respond_to?(:unserialize)
|
146
|
-
type.unserialize(statement.object)
|
147
|
-
when type.is_a?(Symbol) || type.is_a?(String)
|
148
|
-
klass = classize_resource(type)
|
149
|
-
cache[statement.object] = promise { klass.for(statement.object, :_cache => cache) }
|
150
|
-
cache[statement.object]
|
151
|
-
else
|
152
|
-
raise TypeError, "Unable to unserialize #{statement.object} as #{type}"
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
# Build an RDF value from a Ruby value for a property
|
157
|
-
# @private
|
158
|
-
def build_rdf_value(value, type)
|
159
|
-
case
|
160
|
-
when type.respond_to?(:serialize)
|
161
|
-
type.serialize(value)
|
162
|
-
when value && value.class.ancestors.include?(Spira::Resource)
|
163
|
-
klass = classize_resource(type)
|
164
|
-
unless klass.ancestors.include?(value.class)
|
165
|
-
raise TypeError, "#{value} is an instance of #{value.class}, expected #{klass}"
|
166
|
-
end
|
167
|
-
value.subject
|
168
|
-
when type.is_a?(Symbol) || type.is_a?(String)
|
169
|
-
klass = classize_resource(type)
|
170
|
-
else
|
171
|
-
raise TypeError, "Unable to serialize #{value} as #{type}"
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
private
|
176
|
-
|
177
|
-
# Return the appropriate class object for a string or symbol
|
178
|
-
# representation. Throws errors correctly if the given class cannot be
|
179
|
-
# located, or if it is not a Spira::Resource
|
180
|
-
#
|
181
|
-
def classize_resource(type)
|
182
|
-
klass = nil
|
183
|
-
begin
|
184
|
-
klass = qualified_const_get(type.to_s)
|
185
|
-
rescue NameError
|
186
|
-
raise NameError, "Could not find relation class #{type} (referenced as #{type} by #{self})"
|
187
|
-
klass.is_a?(Class) && klass.ancestors.include?(Spira::Resource)
|
188
|
-
end
|
189
|
-
unless klass.is_a?(Class) && klass.ancestors.include?(Spira::Resource)
|
190
|
-
raise TypeError, "#{type} is not a Spira Resource (referenced as #{type} by #{self})"
|
191
|
-
end
|
192
|
-
klass
|
193
|
-
end
|
194
|
-
|
195
|
-
# Resolve a constant from a string, relative to this class' namespace, if
|
196
|
-
# available, and from root, otherwise.
|
197
|
-
#
|
198
|
-
# FIXME: this is not really 'qualified', but it's one of those
|
199
|
-
# impossible-to-name functions. Open to suggestions.
|
200
|
-
#
|
201
|
-
# @author njh
|
202
|
-
# @private
|
203
|
-
def qualified_const_get(str)
|
204
|
-
path = str.to_s.split('::')
|
205
|
-
from_root = path[0].empty?
|
206
|
-
if from_root
|
207
|
-
from_root = []
|
208
|
-
path = path[1..-1]
|
209
|
-
else
|
210
|
-
start_ns = ((Class === self)||(Module === self)) ? self : self.class
|
211
|
-
from_root = start_ns.to_s.split('::')
|
212
|
-
end
|
213
|
-
until from_root.empty?
|
214
|
-
begin
|
215
|
-
return (from_root+path).inject(Object) { |ns,name| ns.const_get(name) }
|
216
|
-
rescue NameError
|
217
|
-
from_root.delete_at(-1)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
path.inject(Object) { |ns,name| ns.const_get(name) }
|
221
|
-
end
|
222
|
-
|
223
|
-
##
|
224
|
-
# Determine the type for a property based on the given type option
|
225
|
-
#
|
226
|
-
# @param [nil, Spira::Type, Constant] type
|
227
|
-
# @return Spira::Type
|
228
|
-
# @private
|
229
|
-
def type_for(type)
|
230
|
-
case
|
231
|
-
when type.nil?
|
232
|
-
Spira::Types::Any
|
233
|
-
when type.is_a?(Symbol) || type.is_a?(String)
|
234
|
-
type
|
235
|
-
when !(Spira.types[type].nil?)
|
236
|
-
Spira.types[type]
|
237
|
-
else
|
238
|
-
raise TypeError, "Unrecognized type: #{type}"
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
##
|
243
|
-
# Determine the predicate for a property based on the given predicate, name, and default vocabulary
|
244
|
-
#
|
245
|
-
# @param [#to_s, #to_uri] predicate
|
246
|
-
# @param [Symbol] name
|
247
|
-
# @return [RDF::URI]
|
248
|
-
# @private
|
249
|
-
def predicate_for(predicate, name)
|
250
|
-
case
|
251
|
-
when predicate.respond_to?(:to_uri) && predicate.to_uri.absolute?
|
252
|
-
predicate
|
253
|
-
when @default_vocabulary.nil?
|
254
|
-
raise ResourceDeclarationError, "A :predicate option is required for types without a default vocabulary"
|
255
|
-
else
|
256
|
-
# FIXME: use rdf.rb smart separator after 0.3.0 release
|
257
|
-
separator = @default_vocabulary.to_s[-1,1] =~ /(\/|#)/ ? '' : '/'
|
258
|
-
RDF::URI.intern(@default_vocabulary.to_s + separator + name.to_s)
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
##
|
263
|
-
# Add getters and setters for a property or list.
|
264
|
-
# @private
|
265
|
-
def add_accessors(name, opts)
|
266
|
-
name_equals = (name.to_s + "=").to_sym
|
267
|
-
|
268
|
-
self.send(:define_method,name_equals) do |arg|
|
269
|
-
attribute_set(name, arg)
|
270
|
-
end
|
271
|
-
self.send(:define_method,name) do
|
272
|
-
attribute_get(name)
|
273
|
-
end
|
274
|
-
|
275
|
-
end
|
276
|
-
|
277
|
-
end
|
278
|
-
end
|
279
|
-
end
|