spira 0.0.2 → 0.0.3

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/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