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
@@ -1,567 +0,0 @@
|
|
1
|
-
require 'rdf/isomorphic'
|
2
|
-
require 'set'
|
3
|
-
|
4
|
-
module Spira
|
5
|
-
module Resource
|
6
|
-
|
7
|
-
##
|
8
|
-
# This module contains instance methods for Spira resources. See
|
9
|
-
# {Spira::Resource} for more information.
|
10
|
-
#
|
11
|
-
# @see Spira::Resource
|
12
|
-
# @see Spira::Resource::ClassMethods
|
13
|
-
# @see Spira::Resource::DSL
|
14
|
-
# @see Spira::Resource::Validations
|
15
|
-
module InstanceMethods
|
16
|
-
|
17
|
-
# Marker for whether or not a field has been set or not; distinguishes
|
18
|
-
# nil and unset.
|
19
|
-
# @private
|
20
|
-
NOT_SET = ::Object.new.freeze
|
21
|
-
|
22
|
-
##
|
23
|
-
# This instance's URI.
|
24
|
-
#
|
25
|
-
# @return [RDF::URI]
|
26
|
-
attr_reader :subject
|
27
|
-
|
28
|
-
##
|
29
|
-
# Initialize a new Spira::Resource instance of this resource class using
|
30
|
-
# a new blank node subject. Accepts a hash of arguments for initial
|
31
|
-
# attributes. To use a URI or existing blank node as a subject, use
|
32
|
-
# {Spira::Resource::ClassMethods#for} instead.
|
33
|
-
#
|
34
|
-
# @param [Hash{Symbol => Any}] opts Default attributes for this instance
|
35
|
-
# @yield [self] Executes a given block and calls `#save!`
|
36
|
-
# @yieldparam [self] self The newly created instance
|
37
|
-
# @see Spira::Resource::ClassMethods#for
|
38
|
-
# @see RDF::URI#as
|
39
|
-
# @see RDF::Node#as
|
40
|
-
def initialize(opts = {})
|
41
|
-
@subject = opts[:_subject] || RDF::Node.new
|
42
|
-
reload(opts)
|
43
|
-
if block_given?
|
44
|
-
yield(self)
|
45
|
-
save!
|
46
|
-
end
|
47
|
-
self
|
48
|
-
end
|
49
|
-
|
50
|
-
##
|
51
|
-
# Reload all attributes for this instance, overwriting or setting
|
52
|
-
# defaults with the given opts. This resource will block if the
|
53
|
-
# underlying repository blocks the next time it accesses attributes.
|
54
|
-
#
|
55
|
-
# @param [Hash{Symbol => Any}] opts
|
56
|
-
# @option opts [Symbol] :any A property name. Sets the given property to the given value.
|
57
|
-
def reload(opts = {})
|
58
|
-
@cache = opts[:_cache] || RDF::Util::Cache.new
|
59
|
-
@cache[subject] = self
|
60
|
-
@dirty = {}
|
61
|
-
@attributes = {}
|
62
|
-
@attributes[:current] = {}
|
63
|
-
@attributes[:copied] = {}
|
64
|
-
self.class.properties.each do |name, predicate|
|
65
|
-
case opts[name].nil?
|
66
|
-
when false
|
67
|
-
attribute_set(name, opts[name])
|
68
|
-
when true
|
69
|
-
@attributes[:copied][name] = NOT_SET
|
70
|
-
end
|
71
|
-
end
|
72
|
-
@attributes[:original] = promise { reload_attributes }
|
73
|
-
end
|
74
|
-
|
75
|
-
##
|
76
|
-
# Load this instance's attributes. Overwrite loaded values with attributes in the given options.
|
77
|
-
#
|
78
|
-
# @return [Hash{Symbol => Any}] attributes
|
79
|
-
# @private
|
80
|
-
def reload_attributes()
|
81
|
-
statements = self.class.repository_or_fail.query(:subject => @subject).to_a
|
82
|
-
attributes = {}
|
83
|
-
|
84
|
-
# Set attributes for each statement corresponding to a predicate
|
85
|
-
self.class.properties.each do |name, property|
|
86
|
-
if self.class.is_list?(name)
|
87
|
-
values = Set.new
|
88
|
-
collection = statements.select{|s| s.subject == @subject && s.predicate == property[:predicate]} unless statements.empty?
|
89
|
-
unless collection.nil?
|
90
|
-
collection.each do |statement|
|
91
|
-
values << self.class.build_value(statement,property[:type], @cache)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
attributes[name] = values
|
95
|
-
else
|
96
|
-
statement = statements.select{|s| s.subject == @subject && s.predicate == property[:predicate]}.first unless statements.empty?
|
97
|
-
attributes[name] = self.class.build_value(statement, property[:type], @cache)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
attributes
|
101
|
-
end
|
102
|
-
|
103
|
-
##
|
104
|
-
# Returns a hash of name => value for this instance's attributes
|
105
|
-
#
|
106
|
-
# @return [Hash{Symbol => Any}] attributes
|
107
|
-
def attributes
|
108
|
-
attributes = {}
|
109
|
-
self.class.properties.keys.each do |property|
|
110
|
-
attributes[property] = attribute_get(property)
|
111
|
-
end
|
112
|
-
attributes
|
113
|
-
end
|
114
|
-
|
115
|
-
##
|
116
|
-
# Remove the given attributes from the repository
|
117
|
-
#
|
118
|
-
# @param [Hash] attributes The hash of attributes to delete
|
119
|
-
# @param [Hash{Symbol => Any}] opts Options for deletion
|
120
|
-
# @option opts [true] :destroy_type Destroys the `RDF.type` statement associated with this class as well
|
121
|
-
# @private
|
122
|
-
def _destroy_attributes(attributes, opts = {})
|
123
|
-
repository = repository_for_attributes(attributes)
|
124
|
-
repository.insert([@subject, RDF.type, self.class.type]) if (self.class.type && opts[:destroy_type])
|
125
|
-
self.class.repository_or_fail.delete(*repository)
|
126
|
-
end
|
127
|
-
|
128
|
-
##
|
129
|
-
# Delete this instance from the repository.
|
130
|
-
#
|
131
|
-
# @param [Symbol] what
|
132
|
-
# @example Delete all fields defined in the model
|
133
|
-
# @object.destroy!
|
134
|
-
# @example Delete all instances of this object as the subject of a triple, including non-model data @object.destroy!
|
135
|
-
# @object.destroy!(:subject)
|
136
|
-
# @example Delete all instances of this object as the object of a triple
|
137
|
-
# @object.destroy!(:object)
|
138
|
-
# @example Delete all triples with this object as the subject or object
|
139
|
-
# @object.destroy!(:completely)
|
140
|
-
# @return [true, false] Whether or not the destroy was successful
|
141
|
-
def destroy!(what = nil)
|
142
|
-
before_destroy if self.respond_to?(:before_destroy)
|
143
|
-
result = case what
|
144
|
-
when nil
|
145
|
-
_destroy_attributes(attributes, :destroy_type => true) != nil
|
146
|
-
when :subject
|
147
|
-
self.class.repository_or_fail.delete([subject, nil, nil]) != nil
|
148
|
-
when :object
|
149
|
-
self.class.repository_or_fail.delete([nil, nil, subject]) != nil
|
150
|
-
when :completely
|
151
|
-
destroy!(:subject) && destroy!(:object)
|
152
|
-
end
|
153
|
-
after_destroy if self.respond_to?(:after_destroy) if result
|
154
|
-
result
|
155
|
-
end
|
156
|
-
|
157
|
-
##
|
158
|
-
# Save changes in this instance to the repository.
|
159
|
-
#
|
160
|
-
# @return [self] self
|
161
|
-
def save!
|
162
|
-
existed = (self.respond_to?(:before_create) || self.respond_to?(:after_create)) && !self.type.nil? && exists?
|
163
|
-
before_create if self.respond_to?(:before_create) && !self.type.nil? && !existed
|
164
|
-
before_save if self.respond_to?(:before_save)
|
165
|
-
# we use the non-raising validate and check it to make a slightly different error message. worth it?...
|
166
|
-
case validate
|
167
|
-
when true
|
168
|
-
_update!
|
169
|
-
when false
|
170
|
-
raise(ValidationError, "Could not save #{self.inspect} due to validation errors: " + errors.each.join(';'))
|
171
|
-
end
|
172
|
-
after_create if self.respond_to?(:after_create) && !self.type.nil? && !existed
|
173
|
-
after_save if self.respond_to?(:after_save)
|
174
|
-
self
|
175
|
-
end
|
176
|
-
|
177
|
-
##
|
178
|
-
# Update multiple attributes of this repository.
|
179
|
-
#
|
180
|
-
# @example Update multiple attributes
|
181
|
-
# person.update(:name => 'test', :age => 10)
|
182
|
-
# #=> person
|
183
|
-
# person.name
|
184
|
-
# #=> 'test'
|
185
|
-
# person.age
|
186
|
-
# #=> 10
|
187
|
-
# person.dirty?
|
188
|
-
# #=> true
|
189
|
-
# @param [Hash{Symbol => Any}] properties
|
190
|
-
# @return [self]
|
191
|
-
def update(properties)
|
192
|
-
properties.each do |property, value|
|
193
|
-
attribute_set(property, value)
|
194
|
-
end
|
195
|
-
after_update if self.respond_to?(:after_update)
|
196
|
-
self
|
197
|
-
end
|
198
|
-
|
199
|
-
##
|
200
|
-
# Equivalent to #update followed by #save!
|
201
|
-
#
|
202
|
-
# @example Update multiple attributes and save the changes
|
203
|
-
# person.update!(:name => 'test', :age => 10)
|
204
|
-
# #=> person
|
205
|
-
# person.name
|
206
|
-
# #=> 'test'
|
207
|
-
# person.age
|
208
|
-
# #=> 10
|
209
|
-
# person.dirty?
|
210
|
-
# #=> false
|
211
|
-
# @param [Hash{Symbol => Any}] properties
|
212
|
-
# @return [self]
|
213
|
-
def update!(properties)
|
214
|
-
update(properties)
|
215
|
-
save!
|
216
|
-
end
|
217
|
-
|
218
|
-
##
|
219
|
-
# Save changes to the repository
|
220
|
-
#
|
221
|
-
# @private
|
222
|
-
def _update!
|
223
|
-
self.class.properties.each do |property, predicate|
|
224
|
-
if dirty?(property)
|
225
|
-
self.class.repository_or_fail.delete([subject, predicate[:predicate], nil])
|
226
|
-
if self.class.is_list?(property)
|
227
|
-
repo = RDF::Repository.new
|
228
|
-
attribute_get(property).each do |value|
|
229
|
-
repo << RDF::Statement.new(subject, predicate[:predicate], self.class.build_rdf_value(value, self.class.properties[property][:type]))
|
230
|
-
end
|
231
|
-
self.class.repository_or_fail.insert(*repo)
|
232
|
-
else
|
233
|
-
self.class.repository_or_fail.insert(RDF::Statement.new(subject, predicate[:predicate], self.class.build_rdf_value(attribute_get(property), self.class.properties[property][:type]))) unless attribute_get(property).nil?
|
234
|
-
end
|
235
|
-
end
|
236
|
-
@attributes[:original][property] = attribute_get(property)
|
237
|
-
@dirty[property] = nil
|
238
|
-
@attributes[:copied][property] = NOT_SET
|
239
|
-
end
|
240
|
-
self.class.repository_or_fail.insert(RDF::Statement.new(@subject, RDF.type, type)) unless type.nil?
|
241
|
-
end
|
242
|
-
|
243
|
-
##
|
244
|
-
# The `RDF.type` associated with this class.
|
245
|
-
#
|
246
|
-
# @return [nil,RDF::URI] The RDF type associated with this instance's class.
|
247
|
-
def type
|
248
|
-
self.class.type
|
249
|
-
end
|
250
|
-
|
251
|
-
##
|
252
|
-
# `type` is a special property which is associated with the class and not
|
253
|
-
# the instance. Always raises a TypeError to try and assign it.
|
254
|
-
#
|
255
|
-
# @raise [TypeError] always
|
256
|
-
def type=(type)
|
257
|
-
raise TypeError, "Cannot reassign RDF.type for #{self}; consider appending to a has_many :types"
|
258
|
-
end
|
259
|
-
|
260
|
-
##
|
261
|
-
# Returns the RDF representation of this resource.
|
262
|
-
#
|
263
|
-
# @return [RDF::Enumerable]
|
264
|
-
def to_rdf
|
265
|
-
self
|
266
|
-
end
|
267
|
-
|
268
|
-
##
|
269
|
-
# A developer-friendly view of this projection
|
270
|
-
#
|
271
|
-
# @private
|
272
|
-
def inspect
|
273
|
-
"<#{self.class}:#{self.object_id} @subject: #{@subject}>"
|
274
|
-
end
|
275
|
-
|
276
|
-
##
|
277
|
-
# Enumerate each RDF statement that makes up this projection. This makes
|
278
|
-
# each instance an `RDF::Enumerable`, with all of the nifty benefits
|
279
|
-
# thereof. See <http://rdf.rubyforge.org/RDF/Enumerable.html> for
|
280
|
-
# information on arguments.
|
281
|
-
#
|
282
|
-
# @see http://rdf.rubyforge.org/RDF/Enumerable.html
|
283
|
-
def each(*args, &block)
|
284
|
-
return enum_for(:each) unless block_given?
|
285
|
-
repository = repository_for_attributes(attributes)
|
286
|
-
repository.insert(RDF::Statement.new(@subject, RDF.type, type)) unless type.nil?
|
287
|
-
repository.each(*args, &block)
|
288
|
-
end
|
289
|
-
|
290
|
-
##
|
291
|
-
# The number of RDF::Statements this projection has.
|
292
|
-
#
|
293
|
-
# @see http://rdf.rubyforge.org/RDF/Enumerable.html#count
|
294
|
-
def count
|
295
|
-
each.size
|
296
|
-
end
|
297
|
-
|
298
|
-
##
|
299
|
-
# Sets the given attribute to the given value.
|
300
|
-
#
|
301
|
-
# @private
|
302
|
-
def attribute_set(name, value)
|
303
|
-
@dirty[name] = true
|
304
|
-
@attributes[:current][name] = value
|
305
|
-
end
|
306
|
-
|
307
|
-
##
|
308
|
-
# Returns true if the given attribute has been changed from the backing store
|
309
|
-
#
|
310
|
-
def dirty?(name = nil)
|
311
|
-
case name
|
312
|
-
when nil
|
313
|
-
self.class.properties.keys.any? { |key| dirty?(key) }
|
314
|
-
else
|
315
|
-
case
|
316
|
-
when @dirty[name] == true
|
317
|
-
true
|
318
|
-
else
|
319
|
-
case @attributes[:copied][name]
|
320
|
-
when NOT_SET
|
321
|
-
false
|
322
|
-
else
|
323
|
-
@attributes[:copied][name] != @attributes[:original][name]
|
324
|
-
end
|
325
|
-
end
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
##
|
330
|
-
# Get the current value for the given attribute
|
331
|
-
#
|
332
|
-
# @private
|
333
|
-
def attribute_get(name)
|
334
|
-
case @dirty[name]
|
335
|
-
when true
|
336
|
-
@attributes[:current][name]
|
337
|
-
else
|
338
|
-
case @attributes[:copied][name].equal?(NOT_SET)
|
339
|
-
when true
|
340
|
-
dup = if @attributes[:original][name].is_a?(Spira::Resource)
|
341
|
-
@attributes[:original][name]
|
342
|
-
else
|
343
|
-
begin
|
344
|
-
@attributes[:original][name].dup
|
345
|
-
rescue TypeError
|
346
|
-
@attributes[:original][name]
|
347
|
-
end
|
348
|
-
end
|
349
|
-
@attributes[:copied][name] = dup
|
350
|
-
when false
|
351
|
-
@attributes[:copied][name]
|
352
|
-
end
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
##
|
357
|
-
# Create an RDF::Repository for the given attributes hash. This could
|
358
|
-
# just as well be a class method but is only used here in #save! and
|
359
|
-
# #destroy!, so it is defined here for simplicity.
|
360
|
-
#
|
361
|
-
# @param [Hash] attributes The attributes to create a repository for
|
362
|
-
# @private
|
363
|
-
def repository_for_attributes(attributes)
|
364
|
-
repo = RDF::Repository.new
|
365
|
-
attributes.each do | name, attribute |
|
366
|
-
if self.class.is_list?(name)
|
367
|
-
new = []
|
368
|
-
attribute.each do |value|
|
369
|
-
value = self.class.build_rdf_value(value, self.class.properties[name][:type])
|
370
|
-
new << RDF::Statement.new(@subject, self.class.properties[name][:predicate], value)
|
371
|
-
end
|
372
|
-
repo.insert(*new)
|
373
|
-
else
|
374
|
-
value = self.class.build_rdf_value(attribute, self.class.properties[name][:type])
|
375
|
-
repo.insert(RDF::Statement.new(@subject, self.class.properties[name][:predicate], value))
|
376
|
-
end
|
377
|
-
end
|
378
|
-
repo
|
379
|
-
end
|
380
|
-
|
381
|
-
##
|
382
|
-
# Compare this instance with another instance. The comparison is done on
|
383
|
-
# an RDF level, and will work across subclasses as long as the attributes
|
384
|
-
# are the same.
|
385
|
-
#
|
386
|
-
# @see http://rdf.rubyforge.org/isomorphic/
|
387
|
-
def ==(other)
|
388
|
-
case other
|
389
|
-
# TODO: define behavior for equality on subclasses.
|
390
|
-
# TODO: should we compare attributes here?
|
391
|
-
when self.class
|
392
|
-
@subject == other.uri
|
393
|
-
when RDF::Enumerable
|
394
|
-
self.isomorphic_with?(other)
|
395
|
-
else
|
396
|
-
false
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
|
-
##
|
401
|
-
# Returns true for :to_uri if this instance's subject is a URI, and false if it is not.
|
402
|
-
# Returns true for :to_node if this instance's subject is a Node, and false if it is not.
|
403
|
-
# Calls super otherwise.
|
404
|
-
#
|
405
|
-
# @private
|
406
|
-
def respond_to?(*args)
|
407
|
-
case args[0]
|
408
|
-
when :to_uri
|
409
|
-
@subject.respond_to?(:to_uri)
|
410
|
-
when :to_node
|
411
|
-
@subject.node?
|
412
|
-
else
|
413
|
-
super(*args)
|
414
|
-
end
|
415
|
-
end
|
416
|
-
|
417
|
-
##
|
418
|
-
# Returns the RDF::URI associated with this instance if this instance's
|
419
|
-
# subject is an RDF::URI, and nil otherwise.
|
420
|
-
#
|
421
|
-
# @return [RDF::URI,nil]
|
422
|
-
def uri
|
423
|
-
@subject.respond_to?(:to_uri) ? @subject : nil
|
424
|
-
end
|
425
|
-
|
426
|
-
##
|
427
|
-
# Returns the URI representation of this resource, if available. If this
|
428
|
-
# resource's subject is a BNode, raises a NoMethodError.
|
429
|
-
#
|
430
|
-
# @return [RDF::URI]
|
431
|
-
# @raise [NoMethodError]
|
432
|
-
def to_uri
|
433
|
-
uri || (raise NoMethodError, "No such method: :to_uri (this instance's subject is not a URI)")
|
434
|
-
end
|
435
|
-
|
436
|
-
##
|
437
|
-
# Returns true if the subject associated with this instance is a blank node.
|
438
|
-
#
|
439
|
-
# @return [true, false]
|
440
|
-
def node?
|
441
|
-
@subject.node?
|
442
|
-
end
|
443
|
-
|
444
|
-
##
|
445
|
-
# Returns the Node subject of this resource, if available. If this
|
446
|
-
# resource's subject is a URI, raises a NoMethodError.
|
447
|
-
#
|
448
|
-
# @return [RDF::Node]
|
449
|
-
# @raise [NoMethodError]
|
450
|
-
def to_node
|
451
|
-
@subject.node? ? @subject : (raise NoMethodError, "No such method: :to_uri (this instance's subject is not a URI)")
|
452
|
-
end
|
453
|
-
|
454
|
-
##
|
455
|
-
# The validation errors collection associated with this instance.
|
456
|
-
#
|
457
|
-
# @return [Spira::Errors]
|
458
|
-
# @see Spira::Errors
|
459
|
-
def errors
|
460
|
-
@errors ||= Spira::Errors.new
|
461
|
-
end
|
462
|
-
|
463
|
-
##
|
464
|
-
# Run any model validations and populate the errors object accordingly.
|
465
|
-
# Returns true if the model is valid, false otherwise
|
466
|
-
#
|
467
|
-
# @return [True, False]
|
468
|
-
def validate
|
469
|
-
unless self.class.validators.empty?
|
470
|
-
errors.clear
|
471
|
-
self.class.validators.each do | validator | self.send(validator) end
|
472
|
-
end
|
473
|
-
errors.empty?
|
474
|
-
end
|
475
|
-
|
476
|
-
##
|
477
|
-
# Run validations on this model and raise a Spira::ValidationError if the validations fail.
|
478
|
-
#
|
479
|
-
# @see #validate
|
480
|
-
# @return true
|
481
|
-
def validate!
|
482
|
-
validate || raise(ValidationError, "Failed to validate #{self.inspect}: " + errors.each.join(';'))
|
483
|
-
end
|
484
|
-
|
485
|
-
##
|
486
|
-
# Returns true if any data exists for this subject in the backing RDF store
|
487
|
-
#
|
488
|
-
# @return [Boolean]
|
489
|
-
def exists?
|
490
|
-
!data.empty?
|
491
|
-
end
|
492
|
-
alias_method :exist?, :exists?
|
493
|
-
|
494
|
-
##
|
495
|
-
# Returns an Enumerator of all RDF data for this subject, not just model data.
|
496
|
-
#
|
497
|
-
# @see #each
|
498
|
-
# @see http://rdf.rubyforge.org/RDF/Enumerable.html
|
499
|
-
# @return [Enumerator]
|
500
|
-
def data
|
501
|
-
self.class.repository.query(:subject => subject)
|
502
|
-
end
|
503
|
-
|
504
|
-
##
|
505
|
-
# Returns a new instance of this class with the new subject instead of self.subject
|
506
|
-
#
|
507
|
-
# @param [RDF::Resource] new_subject
|
508
|
-
# @return [Spira::Resource] copy
|
509
|
-
def copy(new_subject)
|
510
|
-
copy = self.class.for(new_subject)
|
511
|
-
self.class.properties.each_key { |property| copy.attribute_set(property, self.attribute_get(property)) }
|
512
|
-
copy
|
513
|
-
end
|
514
|
-
|
515
|
-
##
|
516
|
-
# Returns a new instance of this class with the new subject instead of
|
517
|
-
# self.subject after saving the new copy to the repository.
|
518
|
-
#
|
519
|
-
# @param [RDF::Resource] new_subject
|
520
|
-
# @return [Spira::Resource, String] copy
|
521
|
-
def copy!(new_subject)
|
522
|
-
copy(new_subject).save!
|
523
|
-
end
|
524
|
-
|
525
|
-
##
|
526
|
-
# Copies all data, including non-model data, about this resource to
|
527
|
-
# another URI. The copy is immediately saved to the repository.
|
528
|
-
#
|
529
|
-
# @param [RDF::Resource] new_subject
|
530
|
-
# @return [Spira::Resource, String] copy
|
531
|
-
def copy_resource!(new_subject)
|
532
|
-
new_subject = self.class.id_for(new_subject)
|
533
|
-
update_repository = RDF::Repository.new
|
534
|
-
data.each do |statement|
|
535
|
-
update_repository << RDF::Statement.new(new_subject, statement.predicate, statement.object)
|
536
|
-
end
|
537
|
-
self.class.repository.insert(update_repository)
|
538
|
-
new_subject.as(self.class)
|
539
|
-
end
|
540
|
-
|
541
|
-
##
|
542
|
-
# Rename this resource in the repository to the new given subject.
|
543
|
-
# Changes are immediately saved to the repository.
|
544
|
-
#
|
545
|
-
# @param [RDF::Resource] new_subject
|
546
|
-
# @return [Spira::Resource, String] new_resource
|
547
|
-
def rename!(new_subject)
|
548
|
-
new = copy_resource!(new_subject)
|
549
|
-
object_statements = self.class.repository.query(:object => subject)
|
550
|
-
update_repository = RDF::Repository.new
|
551
|
-
object_statements.each do |statement|
|
552
|
-
update_repository << RDF::Statement.new(statement.subject, statement.predicate, new.subject)
|
553
|
-
end
|
554
|
-
self.class.repository.insert(update_repository)
|
555
|
-
destroy!(:completely)
|
556
|
-
new
|
557
|
-
end
|
558
|
-
|
559
|
-
## We have defined #each and can do this fun RDF stuff by default
|
560
|
-
include ::RDF::Enumerable, ::RDF::Queryable
|
561
|
-
|
562
|
-
## Include the base validation functions
|
563
|
-
include Spira::Resource::Validations
|
564
|
-
|
565
|
-
end
|
566
|
-
end
|
567
|
-
end
|