spira 0.0.2 → 0.0.3

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