spira 0.0.1.pre → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +151 -29
- data/VERSION +1 -1
- data/lib/spira.rb +86 -1
- data/lib/spira/errors.rb +94 -0
- data/lib/spira/resource.rb +53 -7
- data/lib/spira/resource/class_methods.rb +113 -47
- data/lib/spira/resource/dsl.rb +137 -70
- data/lib/spira/resource/instance_methods.rb +165 -56
- data/lib/spira/resource/validations.rb +32 -8
- data/lib/spira/type.rb +82 -0
- data/lib/spira/types.rb +25 -0
- data/lib/spira/types/any.rb +23 -0
- data/lib/spira/types/boolean.rb +31 -0
- data/lib/spira/types/float.rb +28 -0
- data/lib/spira/types/integer.rb +27 -0
- data/lib/spira/types/string.rb +27 -0
- data/lib/spira/version.rb +23 -0
- metadata +13 -9
data/lib/spira/resource.rb
CHANGED
@@ -1,4 +1,37 @@
|
|
1
1
|
module Spira
|
2
|
+
|
3
|
+
##
|
4
|
+
# Spira::Resource is the main interface to Spira. Classes and modules
|
5
|
+
# include Spira::Resource to create projections of RDF data as a class. For
|
6
|
+
# an overview, see the {file:README}.
|
7
|
+
#
|
8
|
+
# Projections are a mapping of RDF predicates to fields.
|
9
|
+
#
|
10
|
+
# class Person
|
11
|
+
# include Spira::Resource
|
12
|
+
#
|
13
|
+
# property :name, :predicate => FOAF.name
|
14
|
+
# property :age, :predicate => FOAF.age, :type => Integer
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# RDF::URI('http://example.org/people/bob').as(Person) #=> <#Person @uri=http://example.org/people/bob>
|
18
|
+
#
|
19
|
+
# Spira resources include the RDF namespace, and can thus reference all of
|
20
|
+
# the default RDF.rb vocabularies without the RDF:: prefix:
|
21
|
+
#
|
22
|
+
# property :name, :predicate => FOAF.name
|
23
|
+
#
|
24
|
+
# The Spira::Resource documentation is broken into several parts, vaguely
|
25
|
+
# related by functionality:
|
26
|
+
# * {Spira::Resource::DSL} contains methods used during the declaration of a class or module
|
27
|
+
# * {Spira::Resource::ClassMethods} contains class methods for use by declared classes
|
28
|
+
# * {Spira::Resource::InstanceMethods} contains methods for use by instances of Spira resource classes
|
29
|
+
# * {Spira::Resource::Validations} contains some default validation functions
|
30
|
+
#
|
31
|
+
# @see Spira::Resource::DSL
|
32
|
+
# @see Spira::Resource::ClassMethods
|
33
|
+
# @see Spira::Resource::InstanceMethods
|
34
|
+
# @see Spira::Resource::Validations
|
2
35
|
module Resource
|
3
36
|
|
4
37
|
autoload :DSL, 'spira/resource/dsl'
|
@@ -6,20 +39,33 @@ module Spira
|
|
6
39
|
autoload :InstanceMethods, 'spira/resource/instance_methods'
|
7
40
|
autoload :Validations, 'spira/resource/validations'
|
8
41
|
|
42
|
+
##
|
43
|
+
# When a child class includes Spira::Resource, this does the magic to make
|
44
|
+
# it a Spira resource.
|
45
|
+
#
|
46
|
+
# @private
|
9
47
|
def self.included(child)
|
10
|
-
|
11
|
-
|
12
|
-
child
|
13
|
-
|
14
|
-
|
48
|
+
# Don't do inclusion work twice. Checking for the properties accessor is
|
49
|
+
# a proxy for a proper check to see if this is a resource already. Ruby
|
50
|
+
# has already extended the child class' ancestors to include
|
51
|
+
# Spira::Resource by the time we get here.
|
52
|
+
# FIXME: Find a 'more correct' check.
|
53
|
+
unless child.respond_to?(:properties)
|
54
|
+
child.extend DSL
|
55
|
+
child.extend ClassMethods
|
56
|
+
child.instance_eval do
|
57
|
+
class << self
|
58
|
+
attr_accessor :properties, :lists
|
59
|
+
end
|
60
|
+
@properties = {}
|
61
|
+
@lists = {}
|
15
62
|
end
|
16
|
-
@properties = {}
|
17
|
-
@lists = {}
|
18
63
|
end
|
19
64
|
end
|
20
65
|
|
21
66
|
# This lets including classes reference vocabularies without the RDF:: prefix
|
22
67
|
include RDF
|
68
|
+
include Spira::Types
|
23
69
|
include InstanceMethods
|
24
70
|
|
25
71
|
end
|
@@ -1,14 +1,21 @@
|
|
1
1
|
module Spira
|
2
2
|
module Resource
|
3
3
|
|
4
|
-
|
5
|
-
#
|
4
|
+
##
|
5
|
+
# This module contains all class methods available to a declared Spira::Resource class.
|
6
|
+
# {Spira::Resource} contains more information about Spira resources.
|
6
7
|
#
|
8
|
+
# @see Spira::Resource
|
9
|
+
# @see Spira::Resource::InstanceMethods
|
10
|
+
# @see Spira::Resource::DSL
|
7
11
|
module ClassMethods
|
8
|
-
def repository=(repo)
|
9
|
-
@repository = repo
|
10
|
-
end
|
11
12
|
|
13
|
+
##
|
14
|
+
# The current repository for this class
|
15
|
+
#
|
16
|
+
# @param [RDF::Repository] repo The repository
|
17
|
+
# @return [Void]
|
18
|
+
# @private
|
12
19
|
def repository
|
13
20
|
case @repository_name
|
14
21
|
when nil
|
@@ -18,68 +25,127 @@ module Spira
|
|
18
25
|
end
|
19
26
|
end
|
20
27
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
28
|
+
##
|
29
|
+
# Create a new projection instance of this class for the given URI. If a
|
30
|
+
# class has a base_uri given, and the argument is not an `RDF::URI`, the
|
31
|
+
# given identifier will be appended to the base URI.
|
32
|
+
#
|
33
|
+
# Spira does not have 'find' or 'create' functions. As RDF identifiers
|
34
|
+
# are globally unique, they all simply 'are'.
|
35
|
+
#
|
36
|
+
# On calling `for`, a new instance is created for the given URI. The
|
37
|
+
# first time access is attempted on a field, the repository will be
|
38
|
+
# queried for existing attributes, which will be used for the given URI.
|
39
|
+
# Underlying repositories are not accessed at the time of calling `for`.
|
40
|
+
#
|
41
|
+
# A class with a base URI may still be projected for any URI, whether or
|
42
|
+
# not it uses the given resource class' base URI.
|
43
|
+
#
|
44
|
+
# @raise [TypeError] if an RDF type is given in the attributes and one is
|
45
|
+
# given in the attributes.
|
46
|
+
# @raise [ArgumentError] if a non-URI is given and the class does not
|
47
|
+
# have a base URI.
|
48
|
+
# @overload for(uri, attributes = {})
|
49
|
+
# @param [RDF::URI] uri The URI to create an instance for
|
50
|
+
# @param [Hash{Symbol => Any}] attributes Initial attributes
|
51
|
+
# @overload for(identifier, attributes = {})
|
52
|
+
# @param [Any] uri The identifier to append to the base URI for this class
|
53
|
+
# @param [Hash{Symbol => Any}] attributes Initial attributes
|
54
|
+
# @return [Spira::Resource] The newly created instance
|
55
|
+
# @see http://rdf.rubyforge.org/RDF/URI.html
|
56
|
+
def for(identifier, attributes = {})
|
57
|
+
if !self.type.nil? && attributes[:type]
|
58
|
+
raise TypeError, "#{self} has an RDF type, #{self.type}, and cannot accept one as an argument."
|
38
59
|
end
|
39
|
-
|
60
|
+
uri = uri_for(identifier)
|
61
|
+
self.new(uri, attributes)
|
40
62
|
end
|
41
63
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
64
|
+
##
|
65
|
+
# Creates a URI based on a base_uri and string or URI
|
66
|
+
#
|
67
|
+
# @param [Any] Identifier
|
68
|
+
# @return [RDF::URI]
|
69
|
+
# @raise [ArgumentError] If this class cannot create an identifier from the given string
|
70
|
+
# @see http://rdf.rubyforge.org/RDF/URI.html
|
71
|
+
def uri_for(identifier)
|
72
|
+
case identifier
|
47
73
|
when RDF::URI
|
48
74
|
identifier
|
49
75
|
when String
|
50
|
-
|
76
|
+
uri = RDF::URI.new(identifier)
|
77
|
+
return uri if uri.absolute?
|
78
|
+
raise ArgumentError, "Cannot create identifier for #{self} by String without base_uri; RDF::URI required" if self.base_uri.nil?
|
51
79
|
separator = self.base_uri.to_s[-1,1] == "/" ? '' : '/'
|
52
|
-
RDF::URI.
|
80
|
+
RDF::URI.new(self.base_uri.to_s + separator + identifier)
|
53
81
|
else
|
54
|
-
raise ArgumentError, "Cannot
|
55
|
-
end
|
56
|
-
statements = self.repository.query(:subject => uri)
|
57
|
-
if statements.empty?
|
58
|
-
nil
|
59
|
-
else
|
60
|
-
self.new(identifier, :statements => statements)
|
82
|
+
raise ArgumentError, "Cannot create an identifier for #{self} from #{identifier}, expected RDF::URI or String"
|
61
83
|
end
|
62
84
|
end
|
63
|
-
|
85
|
+
|
86
|
+
|
87
|
+
##
|
88
|
+
# The number of URIs projectable as a given class in the repository.
|
89
|
+
# This method is only valid for classes which declare a `type` with the
|
90
|
+
# `type` method in the DSL.
|
91
|
+
#
|
92
|
+
# @raise [TypeError] if the resource class does not have an RDF type declared
|
93
|
+
# @return [Integer] the count
|
94
|
+
# @see Spira::Resource::DSL
|
64
95
|
def count
|
65
96
|
raise TypeError, "Cannot count a #{self} without a reference type URI." if @type.nil?
|
66
97
|
result = repository.query(:predicate => RDF.type, :object => @type)
|
67
98
|
result.count
|
68
99
|
end
|
69
100
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
101
|
+
##
|
102
|
+
# Returns true if the given property is a has_many property, false otherwise
|
103
|
+
#
|
104
|
+
# @return [true, false]
|
105
|
+
def is_list?(property)
|
106
|
+
@lists.has_key?(property)
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Handling inheritance
|
111
|
+
#
|
112
|
+
# @private
|
113
|
+
def inherited(child)
|
114
|
+
child.instance_eval do
|
115
|
+
include Spira::Resource
|
116
|
+
end
|
117
|
+
# FIXME: This is clearly brittle and ugly.
|
118
|
+
[:@base_uri, :@default_vocabulary, :@repository_name, :@type].each do |variable|
|
119
|
+
value = instance_variable_get(variable).nil? ? nil : instance_variable_get(variable).dup
|
120
|
+
child.instance_variable_set(variable, value)
|
121
|
+
end
|
122
|
+
[:@properties, :@lists, :@validators].each do |variable|
|
123
|
+
if child.instance_variable_get(variable).nil?
|
124
|
+
if instance_variable_get(variable).nil?
|
125
|
+
child.instance_variable_set(variable, nil)
|
126
|
+
else
|
127
|
+
child.instance_variable_set(variable, instance_variable_get(variable).dup)
|
128
|
+
end
|
129
|
+
elsif !(instance_variable_get(variable).nil?)
|
130
|
+
child.instance_variable_set(variable, instance_variable_get(variable).dup.merge(child.instance_variable_get(variable)))
|
75
131
|
end
|
76
|
-
attributes[:type] = @type
|
77
132
|
end
|
78
|
-
resource = self.new(name, attributes)
|
79
133
|
end
|
80
134
|
|
81
|
-
|
82
|
-
|
135
|
+
##
|
136
|
+
# Handling module inclusions
|
137
|
+
#
|
138
|
+
# @private
|
139
|
+
def included(child)
|
140
|
+
inherited(child)
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# The list of validation functions for this projection
|
145
|
+
#
|
146
|
+
# @return [Array<Symbol>]
|
147
|
+
def validators
|
148
|
+
@validators ||= []
|
83
149
|
end
|
84
150
|
|
85
151
|
end
|
data/lib/spira/resource/dsl.rb
CHANGED
@@ -1,41 +1,122 @@
|
|
1
1
|
require 'promise'
|
2
2
|
|
3
3
|
module Spira
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
4
|
+
module Resource
|
5
|
+
|
6
|
+
##
|
7
|
+
# This module consists of Spira::Resource class methods which correspond to
|
8
|
+
# the Spira resource class declaration DSL. See {Spira::Resource} for more
|
9
|
+
# information.
|
10
|
+
#
|
11
|
+
# @see Spira::Resource
|
12
|
+
# @see Spira::Resource::ClassMethods
|
13
|
+
# @see Spira::Resource::InstanceMethods
|
14
|
+
# @see Spira::Resource::Validations
|
14
15
|
module DSL
|
15
16
|
|
17
|
+
##
|
18
|
+
# The name of the default repository to use for this class. This
|
19
|
+
# repository will be queried and written to instead of the :default
|
20
|
+
# repository.
|
21
|
+
#
|
22
|
+
# @param [Symbol] name
|
23
|
+
# @return [Void]
|
16
24
|
def default_source(name)
|
17
25
|
@repository_name = name
|
18
26
|
@repository = Spira.repository(name)
|
19
27
|
end
|
20
|
-
|
28
|
+
|
29
|
+
##
|
30
|
+
# The base URI for this class. Attempts to create instances for non-URI
|
31
|
+
# objects will be appended to this base URI.
|
32
|
+
#
|
33
|
+
# @param [String, RDF::URI] base uri
|
34
|
+
# @return [Void]
|
21
35
|
def base_uri(uri = nil)
|
22
36
|
@base_uri = uri unless uri.nil?
|
23
37
|
@base_uri
|
24
38
|
end
|
25
|
-
|
39
|
+
|
40
|
+
##
|
41
|
+
# The default vocabulary for this class. Setting a default vocabulary
|
42
|
+
# will allow properties to be defined without a `:predicate` option.
|
43
|
+
# Predicates will instead be created by appending the property name to
|
44
|
+
# the given string.
|
45
|
+
#
|
46
|
+
# @param [String, RDF::URI] base uri
|
47
|
+
# @return [Void]
|
26
48
|
def default_vocabulary(uri)
|
27
49
|
@default_vocabulary = uri
|
28
50
|
end
|
29
51
|
|
52
|
+
|
53
|
+
##
|
54
|
+
# Add a property to this class. A property is an accessor field that
|
55
|
+
# represents an RDF predicate.
|
56
|
+
#
|
57
|
+
# @example A simple string property
|
58
|
+
# property :name, :predicate => FOAF.name, :type => String
|
59
|
+
# @example A property which defaults to {Spira::Types::Any}
|
60
|
+
# property :name, :predicate => FOAF.name
|
61
|
+
# @example An integer property
|
62
|
+
# property :age, :predicate => FOAF.age, :type => Integer
|
63
|
+
# @param [Symbol] name The name of this property
|
64
|
+
# @param [Hash{Symbol => Any}] opts property options
|
65
|
+
# @option opts [RDF::URI] :predicate The RDF predicate which will refer to this property
|
66
|
+
# @option opts [Spira::Type, String] :type (Spira::Types::Any) The
|
67
|
+
# type for this property. If a Spira::Type is given, that class will be
|
68
|
+
# used to serialize and unserialize values. If a String is given, it
|
69
|
+
# should be the String form of a Spira::Resource class name (Strings are
|
70
|
+
# used to prevent issues with load order).
|
71
|
+
# @see Spira::Types
|
72
|
+
# @see Spira::Type
|
73
|
+
# @return [Void]
|
30
74
|
def property(name, opts = {} )
|
31
75
|
add_accessors(name,opts,:hash_accessors)
|
32
76
|
end
|
33
77
|
|
78
|
+
##
|
79
|
+
# The plural form of `property`. `Has_many` has the same options as
|
80
|
+
# `property`, but instead of a single value, a Ruby Array of objects will
|
81
|
+
# be created instead. Be warned that this should be a Set to match RDF
|
82
|
+
# semantics, but this is not currently implemented. Duplicate values of
|
83
|
+
# an array will be lost on save.
|
84
|
+
#
|
85
|
+
# @see Spira::Resource::DSL#property
|
34
86
|
def has_many(name, opts = {})
|
35
87
|
add_accessors(name,opts,:hash_accessors)
|
36
88
|
@lists[name] = true
|
37
89
|
end
|
38
90
|
|
91
|
+
##
|
92
|
+
# Validate this model with the given validator function name.
|
93
|
+
#
|
94
|
+
# @example
|
95
|
+
# class Person
|
96
|
+
# include Spira::Resource
|
97
|
+
# property :name, :predicate => FOAF.name
|
98
|
+
# validate :is_awesome
|
99
|
+
# def is_awesome
|
100
|
+
# assert(name =~ /Thor/, :name, "not awesome")
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
# @param [Symbol] validator
|
104
|
+
# @return [Void]
|
105
|
+
def validate(validator)
|
106
|
+
validators << validator unless validators.include?(validator)
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
##
|
111
|
+
# Associate an RDF type with this class. RDF resources can be multiple
|
112
|
+
# types at once, but if they have an `RDF.type` statement for the given
|
113
|
+
# URI, this class can #count them.
|
114
|
+
#
|
115
|
+
# @param [RDF::URI] uri The URI object of the `RDF.type` triple
|
116
|
+
# @return [Void]
|
117
|
+
# @see http://rdf.rubyforge.net/RDF/URI.html
|
118
|
+
# @see http://rdf.rubyforge.org/RDF.html#type-class_method
|
119
|
+
# @see Spira::Resource::ClassMethods#count
|
39
120
|
def type(uri = nil)
|
40
121
|
unless uri.nil?
|
41
122
|
@type = case uri
|
@@ -48,41 +129,55 @@ module Spira
|
|
48
129
|
@type
|
49
130
|
end
|
50
131
|
|
132
|
+
# Build a Ruby value from an RDF value.
|
51
133
|
#
|
52
134
|
# @private
|
53
|
-
def build_value(statement, type)
|
135
|
+
def build_value(statement, type, existing_relation = nil)
|
54
136
|
case
|
55
137
|
when statement == nil
|
56
138
|
nil
|
57
|
-
when type
|
58
|
-
statement.object
|
59
|
-
when type
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
139
|
+
when type.is_a?(Class) && type.ancestors.include?(Spira::Type)
|
140
|
+
type.unserialize(statement.object)
|
141
|
+
when type.is_a?(Symbol) || type.is_a?(String)
|
142
|
+
klass = begin
|
143
|
+
Kernel.const_get(type.to_s)
|
144
|
+
rescue NameError
|
145
|
+
unless klass.is_a?(Class) && klass.ancestors.include?(Spira::Resource)
|
146
|
+
raise TypeError, "#{type} is not a Spira Resource (referenced as #{type} by #{self}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
case
|
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
|
156
|
+
else
|
157
|
+
raise TypeError, "Unable to unserialize #{statement.object} for #{type}"
|
65
158
|
end
|
66
159
|
end
|
67
160
|
|
161
|
+
# Build an RDF value from a Ruby value for a property
|
68
162
|
# @private
|
69
163
|
def build_rdf_value(value, type)
|
70
164
|
case
|
71
|
-
when
|
165
|
+
when type.is_a?(Class) && type.ancestors.include?(Spira::Type)
|
166
|
+
type.serialize(value)
|
167
|
+
when value && value.class.ancestors.include?(Spira::Resource)
|
72
168
|
value.uri
|
73
|
-
when type == nil
|
74
|
-
value
|
75
169
|
when type == RDF::URI && value.is_a?(RDF::URI)
|
76
170
|
value
|
77
|
-
when type.is_a?(RDF::URI)
|
78
|
-
RDF::Literal.new(value, :datatype => type)
|
79
171
|
else
|
80
|
-
|
172
|
+
raise TypeError, "Unable to serialize #{value} for #{type}"
|
81
173
|
end
|
82
174
|
end
|
83
175
|
|
84
176
|
private
|
85
177
|
|
178
|
+
##
|
179
|
+
# Add getters and setters for a property or list.
|
180
|
+
# @private
|
86
181
|
def add_accessors(name, opts, accessors_method)
|
87
182
|
predicate = case
|
88
183
|
when opts[:predicate]
|
@@ -93,8 +188,16 @@ module Spira
|
|
93
188
|
separator = @default_vocabulary.to_s[-1,1] == "/" ? '' : '/'
|
94
189
|
RDF::URI.new(@default_vocabulary.to_s + separator + name.to_s)
|
95
190
|
end
|
96
|
-
|
97
|
-
|
191
|
+
type = case
|
192
|
+
when opts[:type].nil?
|
193
|
+
Spira::Types::Any
|
194
|
+
when opts[:type].is_a?(Symbol) || opts[:type].is_a?(String)
|
195
|
+
opts[:type]
|
196
|
+
when !(Spira.types[opts[:type]].nil?)
|
197
|
+
Spira.types[opts[:type]]
|
198
|
+
else
|
199
|
+
raise TypeError, "Unrecognized type: #{opts[:type]}"
|
200
|
+
end
|
98
201
|
@properties[name] = {}
|
99
202
|
@properties[name][:predicate] = predicate
|
100
203
|
@properties[name][:type] = type
|
@@ -106,6 +209,11 @@ module Spira
|
|
106
209
|
|
107
210
|
end
|
108
211
|
|
212
|
+
##
|
213
|
+
# Getter and Setter methods for predicates.
|
214
|
+
# FIXME: this and add_accessors are from an older version in which
|
215
|
+
# multiple versions of accessors existed, and can be refactored.
|
216
|
+
# @private
|
109
217
|
def hash_accessors(name, predicate, type)
|
110
218
|
setter = lambda do |arg|
|
111
219
|
attribute_set(name,arg)
|
@@ -118,47 +226,6 @@ module Spira
|
|
118
226
|
[getter, setter]
|
119
227
|
end
|
120
228
|
|
121
|
-
def list_accessors(name, predicate, type)
|
122
|
-
|
123
|
-
setter = lambda do |arg|
|
124
|
-
old = @repo.query(:subject => @uri, :predicate => predicate)
|
125
|
-
@repo.delete(*old.to_a) unless old.empty?
|
126
|
-
new = []
|
127
|
-
arg.each do |value|
|
128
|
-
value = self.class.build_rdf_value(value, type)
|
129
|
-
new << RDF::Statement.new(@uri, predicate, value)
|
130
|
-
end
|
131
|
-
@repo.insert(*new)
|
132
|
-
end
|
133
|
-
|
134
|
-
getter = lambda do
|
135
|
-
values = []
|
136
|
-
statements = @repo.query(:subject => @uri, :predicate => predicate)
|
137
|
-
statements.each do |statement|
|
138
|
-
values << self.class.build_value(statement, type)
|
139
|
-
end
|
140
|
-
values
|
141
|
-
end
|
142
|
-
|
143
|
-
[getter, setter]
|
144
|
-
end
|
145
|
-
|
146
|
-
def single_accessors(name, predicate, type)
|
147
|
-
setter = lambda do |arg|
|
148
|
-
old = @repo.query(:subject => @uri, :predicate => predicate)
|
149
|
-
@repo.delete(*old.to_a) unless old.empty?
|
150
|
-
arg = self.class.build_rdf_value(arg, type)
|
151
|
-
@repo.insert(RDF::Statement.new(@uri, predicate, arg))
|
152
|
-
end
|
153
|
-
|
154
|
-
getter = lambda do
|
155
|
-
statement = @repo.query(:subject => @uri, :predicate => predicate).first
|
156
|
-
self.class.build_value(statement, type)
|
157
|
-
end
|
158
|
-
|
159
|
-
[getter, setter]
|
160
|
-
end
|
161
|
-
|
162
229
|
end
|
163
230
|
end
|
164
231
|
end
|