spira 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.md +18 -0
- data/README +2 -2
- data/VERSION +1 -1
- data/lib/spira.rb +8 -6
- data/lib/spira/resource/class_methods.rb +24 -5
- data/lib/spira/resource/dsl.rb +7 -4
- data/lib/spira/resource/instance_methods.rb +10 -2
- data/lib/spira/version.rb +1 -1
- metadata +3 -3
data/CHANGES.md
CHANGED
@@ -1,4 +1,22 @@
|
|
1
1
|
# Changelog for Spira <http://github.com/datagraph/spira>
|
2
|
+
## 0.0.8
|
3
|
+
* Remove type checking for repository addition. More power in return for
|
4
|
+
slightly more difficult error messages.
|
5
|
+
* Repositories added via klass, *arg are now only instantiated on first use
|
6
|
+
instead of immediately.
|
7
|
+
* RDF::URI#as, RDF::Node#as, Resource.for, and Resource#new can now all accept
|
8
|
+
a block, which yields the new instance and saves it after the block.
|
9
|
+
* Clarify error message when default repository is not setup
|
10
|
+
* Added a weak-reference identity map for each instance. Any circular references in
|
11
|
+
relations will now return the original object instead of querying for a new
|
12
|
+
one.
|
13
|
+
* Use a weak-reference identity map when iterating by class.
|
14
|
+
* When serializing/unserializing, duck typing (:serialize, :unserialize) is now
|
15
|
+
permitted.
|
16
|
+
|
17
|
+
## 0.0.7
|
18
|
+
* Added Resource.[], an alias for Resource.for
|
19
|
+
* Resource.each now correctly raises an exception when a repository isn't found
|
2
20
|
|
3
21
|
## 0.0.6
|
4
22
|
* Added #exists?, which returns a boolean if an instance exists in
|
data/README
CHANGED
@@ -90,7 +90,7 @@ Then use your model classes, in a way more or less similar to any number of ORMs
|
|
90
90
|
artist.cds = [cd]
|
91
91
|
artist.save!
|
92
92
|
|
93
|
-
queen =
|
93
|
+
queen = Artist.for('queen')
|
94
94
|
hits = CD.for 'queens-greatest-hits'
|
95
95
|
hits.artist == artist == queen
|
96
96
|
|
@@ -117,7 +117,7 @@ Any call to 'for' with a valid identifier will always return an object with nil
|
|
117
117
|
fields. It's a way of looking at a given resource, not a closed-world mapping
|
118
118
|
to one.
|
119
119
|
|
120
|
-
You can
|
120
|
+
You can also use blank nodes more or less as you would a URI:
|
121
121
|
|
122
122
|
remix_artist = Artist.for(RDF::Node.new)
|
123
123
|
# => <Artist @subject=#<RDF::Node:0xd1d314(_:g13751060)>>
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.8
|
data/lib/spira.rb
CHANGED
@@ -60,12 +60,10 @@ module Spira
|
|
60
60
|
# @see RDF::Repository
|
61
61
|
def add_repository(name, klass, *args)
|
62
62
|
repositories[name] = case klass
|
63
|
-
when RDF::Repository
|
64
|
-
klass
|
65
63
|
when Class
|
66
|
-
klass.new(*args)
|
64
|
+
promise { klass.new(*args) }
|
67
65
|
else
|
68
|
-
|
66
|
+
klass
|
69
67
|
end
|
70
68
|
if (name == :default) && settings[:repositories][name].nil?
|
71
69
|
warn "WARNING: Adding nil default repository"
|
@@ -128,10 +126,12 @@ module RDF
|
|
128
126
|
# RDF::URI('http://example.org/person/bob').as(Person)
|
129
127
|
# @param [Class] klass
|
130
128
|
# @param [*Any] args Any arguments to pass to klass.for
|
129
|
+
# @yield [self] Executes a given block and calls `#save!`
|
130
|
+
# @yieldparam [self] self The newly created instance
|
131
131
|
# @return [Klass] An instance of klass
|
132
|
-
def as(klass, *args)
|
132
|
+
def as(klass, *args, &block)
|
133
133
|
raise ArgumentError, "#{klass} is not a Spira resource" unless klass.is_a?(Class) && klass.ancestors.include?(Spira::Resource)
|
134
|
-
klass.for(self, *args)
|
134
|
+
klass.for(self, *args, &block)
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
@@ -144,6 +144,8 @@ module RDF
|
|
144
144
|
# RDF::Node.new.as(Person)
|
145
145
|
# @param [Class] klass
|
146
146
|
# @param [*Any] args Any arguments to pass to klass.for
|
147
|
+
# @yield [self] Executes a given block and calls `#save!`
|
148
|
+
# @yieldparam [self] self The newly created instance
|
147
149
|
# @return [Klass] An instance of klass
|
148
150
|
def as(klass, *args)
|
149
151
|
raise ArgumentError, "#{klass} is not a Spira resource" unless klass.is_a?(Class) && klass.ancestors.include?(Spira::Resource)
|
@@ -32,7 +32,7 @@ module Spira
|
|
32
32
|
# @return [RDF::Repository]
|
33
33
|
# @private
|
34
34
|
def repository_or_fail
|
35
|
-
repository || (raise Spira::NoRepositoryError, "#{self} is configured to use
|
35
|
+
repository || (raise Spira::NoRepositoryError, "#{self} is configured to use :#{@repository_name || 'default'} as a repository, but it has not been set.")
|
36
36
|
end
|
37
37
|
|
38
38
|
##
|
@@ -61,14 +61,16 @@ module Spira
|
|
61
61
|
# @overload for(identifier, attributes = {})
|
62
62
|
# @param [Any] uri The identifier to append to the base URI for this class
|
63
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
|
64
66
|
# @return [Spira::Resource] The newly created instance
|
65
67
|
# @see http://rdf.rubyforge.org/RDF/URI.html
|
66
|
-
def for(identifier, attributes = {})
|
68
|
+
def for(identifier, attributes = {}, &block)
|
67
69
|
if !self.type.nil? && attributes[:type]
|
68
70
|
raise TypeError, "#{self} has an RDF type, #{self.type}, and cannot accept one as an argument."
|
69
71
|
end
|
70
72
|
subject = id_for(identifier)
|
71
|
-
self.new(attributes.merge(:_subject => subject))
|
73
|
+
instance = self.new(attributes.merge(:_subject => subject), &block)
|
72
74
|
end
|
73
75
|
|
74
76
|
##
|
@@ -124,6 +126,23 @@ module Spira
|
|
124
126
|
repository.query(:predicate => RDF.type, :object => @type).subjects.count
|
125
127
|
end
|
126
128
|
|
129
|
+
##
|
130
|
+
# A cache of iterated instances of this projection
|
131
|
+
#
|
132
|
+
# @return [RDF::Util::Cache]
|
133
|
+
# @private
|
134
|
+
def cache
|
135
|
+
@cache ||= RDF::Util::Cache.new
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Clear the iteration cache
|
140
|
+
#
|
141
|
+
# @return [void]
|
142
|
+
def reload
|
143
|
+
@cache = nil
|
144
|
+
end
|
145
|
+
|
127
146
|
##
|
128
147
|
# Enumerate over all resources projectable as this class. This method is
|
129
148
|
# only valid for classes which declare a `type` with the `type` method in
|
@@ -146,12 +165,12 @@ module Spira
|
|
146
165
|
enum_for(:each)
|
147
166
|
else
|
148
167
|
repository_or_fail.query(:predicate => RDF.type, :object => @type).each_subject do |subject|
|
149
|
-
|
168
|
+
self.cache[subject] ||= self.for(subject)
|
169
|
+
block.call(cache[subject])
|
150
170
|
end
|
151
171
|
end
|
152
172
|
end
|
153
173
|
|
154
|
-
|
155
174
|
##
|
156
175
|
# Returns true if the given property is a has_many property, false otherwise
|
157
176
|
#
|
data/lib/spira/resource/dsl.rb
CHANGED
@@ -132,15 +132,18 @@ module Spira
|
|
132
132
|
# Build a Ruby value from an RDF value.
|
133
133
|
#
|
134
134
|
# @private
|
135
|
-
def build_value(statement, type)
|
135
|
+
def build_value(statement, type, cache)
|
136
136
|
case
|
137
137
|
when statement == nil
|
138
138
|
nil
|
139
|
-
when
|
139
|
+
when !cache[statement.object].nil?
|
140
|
+
cache[statement.object]
|
141
|
+
when type.respond_to?(:unserialize)
|
140
142
|
type.unserialize(statement.object)
|
141
143
|
when type.is_a?(Symbol) || type.is_a?(String)
|
142
144
|
klass = classize_resource(type)
|
143
|
-
promise { klass.for(statement.object) }
|
145
|
+
cache[statement.object] = promise { klass.for(statement.object, :_cache => cache) }
|
146
|
+
cache[statement.object]
|
144
147
|
else
|
145
148
|
raise TypeError, "Unable to unserialize #{statement.object} as #{type}"
|
146
149
|
end
|
@@ -150,7 +153,7 @@ module Spira
|
|
150
153
|
# @private
|
151
154
|
def build_rdf_value(value, type)
|
152
155
|
case
|
153
|
-
when type.
|
156
|
+
when type.respond_to?(:serialize)
|
154
157
|
type.serialize(value)
|
155
158
|
when value && value.class.ancestors.include?(Spira::Resource)
|
156
159
|
klass = classize_resource(type)
|
@@ -27,12 +27,18 @@ module Spira
|
|
27
27
|
# {Spira::Resource::ClassMethods#for} instead.
|
28
28
|
#
|
29
29
|
# @param [Hash{Symbol => Any}] opts Default attributes for this instance
|
30
|
+
# @yield [self] Executes a given block and calls `#save!`
|
31
|
+
# @yieldparam [self] self The newly created instance
|
30
32
|
# @see Spira::Resource::ClassMethods#for
|
31
33
|
# @see RDF::URI#as
|
32
34
|
# @see RDF::Node#as
|
33
35
|
def initialize(opts = {})
|
34
36
|
@subject = opts[:_subject] || RDF::Node.new
|
35
37
|
reload(opts)
|
38
|
+
if block_given?
|
39
|
+
yield(self)
|
40
|
+
save!
|
41
|
+
end
|
36
42
|
end
|
37
43
|
|
38
44
|
##
|
@@ -43,6 +49,8 @@ module Spira
|
|
43
49
|
# @param [Hash{Symbol => Any}] opts
|
44
50
|
# @option opts [Symbol] :any A property name. Sets the given property to the given value.
|
45
51
|
def reload(opts = {})
|
52
|
+
@cache = opts[:_cache] || RDF::Util::Cache.new
|
53
|
+
@cache[subject] = self
|
46
54
|
@dirty = {}
|
47
55
|
# We need to save all attributes twice to track state changes in
|
48
56
|
# mutable objects, like lists
|
@@ -70,14 +78,14 @@ module Spira
|
|
70
78
|
collection = statements.query(:subject => @subject, :predicate => property[:predicate]) unless statements.empty?
|
71
79
|
unless collection.nil?
|
72
80
|
collection.each do |statement|
|
73
|
-
values << self.class.build_value(statement,property[:type])
|
81
|
+
values << self.class.build_value(statement,property[:type], @cache)
|
74
82
|
end
|
75
83
|
end
|
76
84
|
attributes[:current][name] = values
|
77
85
|
attributes[:original][name] = values.dup
|
78
86
|
else
|
79
87
|
statement = statements.query(:subject => @subject, :predicate => property[:predicate]).first unless statements.empty?
|
80
|
-
attributes[:current][name] = self.class.build_value(statement, property[:type])
|
88
|
+
attributes[:current][name] = self.class.build_value(statement, property[:type], @cache)
|
81
89
|
|
82
90
|
# Lots of things like Fixnums and Nil can't be dup'd, but they are
|
83
91
|
# all immutable, so if we can't dup, it's no problem for dirty tracking.
|
data/lib/spira/version.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 8
|
9
|
+
version: 0.0.8
|
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-09-
|
18
|
+
date: 2010-09-22 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|