spira 0.0.12 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|