activefacts-api 0.9.5 → 0.9.6
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/VERSION +1 -1
- data/activefacts-api.gemspec +4 -3
- data/lib/activefacts/api/constellation.rb +19 -10
- data/lib/activefacts/api/date.rb +3 -1
- data/lib/activefacts/api/entity.rb +67 -14
- data/lib/activefacts/api/exceptions.rb +56 -8
- data/lib/activefacts/api/guid.rb +1 -1
- data/lib/activefacts/api/instance.rb +15 -5
- data/lib/activefacts/api/instance_index.rb +1 -1
- data/lib/activefacts/api/numeric.rb +2 -2
- data/lib/activefacts/api/object_type.rb +68 -56
- data/lib/activefacts/api/standard_types.rb +10 -4
- data/lib/activefacts/api/value.rb +5 -6
- data/lib/activefacts/api/vocabulary.rb +9 -5
- data/spec/constellation/constellation_spec.rb +5 -5
- data/spec/fact_type/role_values_spec.rb +1 -1
- data/spec/identification_scheme/identification_spec.rb +1 -1
- data/spec/metadata_spec.rb +2 -1
- data/spec/spec_helper.rb +2 -1
- data/spec/support/reduce_exceptions_helper.rb +29 -0
- metadata +152 -154
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.9.
|
|
1
|
+
0.9.6
|
data/activefacts-api.gemspec
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
Gem::Specification.new do |s|
|
|
7
7
|
s.name = "activefacts-api"
|
|
8
|
-
s.version = "0.9.
|
|
8
|
+
s.version = "0.9.6"
|
|
9
9
|
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
11
|
s.authors = ["Clifford Heath"]
|
|
12
|
-
s.date = "2013-01-
|
|
12
|
+
s.date = "2013-01-22"
|
|
13
13
|
s.description = "\nThe ActiveFacts API is a Ruby DSL for managing constellations of elementary facts.\nEach fact is either existential (a value or an entity), characteristic (boolean) or\nbinary relational (A rel B). Relational facts are consistently co-referenced, so you\ncan traverse them efficiently in any direction. Each constellation maintains constraints\nover the fact population.\n"
|
|
14
14
|
s.email = "clifford.heath@gmail.com"
|
|
15
15
|
s.extra_rdoc_files = [
|
|
@@ -62,7 +62,8 @@ Gem::Specification.new do |s|
|
|
|
62
62
|
"spec/object_type/value_type/numeric_spec.rb",
|
|
63
63
|
"spec/object_type/value_type/value_type_spec.rb",
|
|
64
64
|
"spec/simplecov_helper.rb",
|
|
65
|
-
"spec/spec_helper.rb"
|
|
65
|
+
"spec/spec_helper.rb",
|
|
66
|
+
"spec/support/reduce_exceptions_helper.rb"
|
|
66
67
|
]
|
|
67
68
|
s.homepage = "http://github.com/cjheath/activefacts-api"
|
|
68
69
|
s.licenses = ["MIT"]
|
|
@@ -34,8 +34,17 @@ module ActiveFacts
|
|
|
34
34
|
class Constellation
|
|
35
35
|
attr_reader :vocabulary
|
|
36
36
|
|
|
37
|
-
def
|
|
38
|
-
|
|
37
|
+
def invalid_object_type klass
|
|
38
|
+
case
|
|
39
|
+
when !klass.is_a?(Class)
|
|
40
|
+
'is not a Class'
|
|
41
|
+
when klass.modspace != @vocabulary
|
|
42
|
+
"is defined in #{klass.modspace}, not #{@vocabulary.name}"
|
|
43
|
+
when !klass.respond_to?(:assert_instance)
|
|
44
|
+
"is not declared as an object type"
|
|
45
|
+
else
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
39
48
|
end
|
|
40
49
|
|
|
41
50
|
# "instances" is an index (keyed by the Class object) of indexes to instances.
|
|
@@ -43,8 +52,8 @@ module ActiveFacts
|
|
|
43
52
|
# The method_missing definition supports the syntax: c.MyClass.each{|k, v| ... }
|
|
44
53
|
def instances
|
|
45
54
|
@instances ||= Hash.new do |h,k|
|
|
46
|
-
|
|
47
|
-
raise
|
|
55
|
+
if reason = invalid_object_type(k)
|
|
56
|
+
raise InvalidObjectType.new(@vocabulary, k, reason)
|
|
48
57
|
end
|
|
49
58
|
h[k] = InstanceIndex.new(self, k)
|
|
50
59
|
end
|
|
@@ -98,7 +107,7 @@ module ActiveFacts
|
|
|
98
107
|
end
|
|
99
108
|
|
|
100
109
|
def has_candidate klass, key
|
|
101
|
-
@candidates && @candidates.detect{|c| c.is_a?(klass) && c.identifying_role_values == key }
|
|
110
|
+
@candidates && @candidates.detect{|c| c.is_a?(klass) && c.identifying_role_values(klass) == key }
|
|
102
111
|
end
|
|
103
112
|
|
|
104
113
|
# Create a new empty Constellation over the given Vocabulary
|
|
@@ -155,11 +164,11 @@ module ActiveFacts
|
|
|
155
164
|
# With parameters, assert an instance of the object_type identified by the values passed as args.
|
|
156
165
|
def method_missing(m, *args, &b)
|
|
157
166
|
klass = @vocabulary.const_get(m)
|
|
158
|
-
if
|
|
167
|
+
if invalid_object_type klass
|
|
168
|
+
super
|
|
169
|
+
else
|
|
159
170
|
define_class_accessor m, klass
|
|
160
171
|
send(m, *args, &b)
|
|
161
|
-
else
|
|
162
|
-
super
|
|
163
172
|
end
|
|
164
173
|
end
|
|
165
174
|
|
|
@@ -188,9 +197,9 @@ module ActiveFacts
|
|
|
188
197
|
role_values =
|
|
189
198
|
single_roles.map{|role|
|
|
190
199
|
value =
|
|
191
|
-
|
|
200
|
+
if instance.respond_to?(role)
|
|
192
201
|
value = instance.send(role)
|
|
193
|
-
|
|
202
|
+
else
|
|
194
203
|
instance.class.roles(role) # This role has not yet been realised
|
|
195
204
|
end
|
|
196
205
|
[ role_name = role.to_s.camelcase, value ]
|
data/lib/activefacts/api/date.rb
CHANGED
|
@@ -26,7 +26,7 @@ module ActiveFacts
|
|
|
26
26
|
# The identifying roles of secondary supertypes must also be assigned
|
|
27
27
|
# here.
|
|
28
28
|
def initialize(arg_hash)
|
|
29
|
-
raise "
|
|
29
|
+
raise ArgumentError.new("#{self}.new expects a hash. You should use assert instead anyhow") unless arg_hash.is_a?(Hash)
|
|
30
30
|
|
|
31
31
|
super(arg_hash)
|
|
32
32
|
|
|
@@ -129,9 +129,10 @@ module ActiveFacts
|
|
|
129
129
|
|
|
130
130
|
# Return the array of the values of this instance's identifying roles
|
|
131
131
|
def identifying_role_values(klass = self.class)
|
|
132
|
-
klass.
|
|
133
|
-
value = send(
|
|
134
|
-
|
|
132
|
+
klass.identifying_roles.map do |role|
|
|
133
|
+
value = send(role.name)
|
|
134
|
+
counterpart_class = role.counterpart && role.counterpart.object_type
|
|
135
|
+
value.identifying_role_values(counterpart_class)
|
|
135
136
|
end
|
|
136
137
|
end
|
|
137
138
|
|
|
@@ -152,6 +153,56 @@ module ActiveFacts
|
|
|
152
153
|
roles_hash
|
|
153
154
|
end
|
|
154
155
|
|
|
156
|
+
# This role is identifying, so if is changed, not only
|
|
157
|
+
# must the current object be re-indexed, but also entities
|
|
158
|
+
# identified by this entity. Save the current key and
|
|
159
|
+
# class for each such instance.
|
|
160
|
+
# This function is transitive!
|
|
161
|
+
def analyse_impacts role
|
|
162
|
+
impacts = []
|
|
163
|
+
|
|
164
|
+
# Consider the object itself and all its supertypes
|
|
165
|
+
([self.class]+self.class.supertypes_transitive).map do |supertype|
|
|
166
|
+
next unless supertype.identifying_roles.include?(role)
|
|
167
|
+
|
|
168
|
+
old_key = identifying_role_values(supertype)
|
|
169
|
+
# puts "Need to reindex #{self.class} as #{supertype} from #{old_key.inspect}"
|
|
170
|
+
impacts << [supertype, self, old_key]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Now consider objects whose identifiers include this object.
|
|
174
|
+
# Find our roles in those identifiers first.
|
|
175
|
+
impacted_roles = []
|
|
176
|
+
self.class.all_roles.each do |n, role|
|
|
177
|
+
if role.counterpart && role.counterpart.is_identifying
|
|
178
|
+
# puts "Changing #{role.inspect} affects #{role.inspect}"
|
|
179
|
+
impacted_roles << role
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
impacted_roles.each do |role|
|
|
184
|
+
affected_instances = Array(instance_variable_get(role.variable))
|
|
185
|
+
# puts "considering #{affected_instances.size} #{role.object_type.name} instances that include #{role.inspect}: #{affected_instances.map(&:identifying_role_values).inspect}"
|
|
186
|
+
affected_instances.each do |counterpart|
|
|
187
|
+
impacts.concat(counterpart.analyse_impacts(role.counterpart))
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
impacts
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def apply_impacts impacts
|
|
194
|
+
impacts.each do |klass, entity, old_key|
|
|
195
|
+
instance_index = entity.constellation.instances[klass]
|
|
196
|
+
new_key = entity.identifying_role_values(klass)
|
|
197
|
+
# puts "Reindexing #{klass} from #{old_key.inspect} to #{new_key.inspect}"
|
|
198
|
+
|
|
199
|
+
if new_key != old_key
|
|
200
|
+
instance_index.delete(old_key)
|
|
201
|
+
instance_index[new_key] = entity
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
155
206
|
# All classes that become Entity types receive the methods of this class as class methods:
|
|
156
207
|
module ClassMethods
|
|
157
208
|
include Instance::ClassMethods
|
|
@@ -190,21 +241,22 @@ module ActiveFacts
|
|
|
190
241
|
|
|
191
242
|
def check_supertype_identifiers_match instance, arg_hash
|
|
192
243
|
supertypes_transitive.each do |supertype|
|
|
193
|
-
supertype.
|
|
194
|
-
next unless arg_hash.include?(
|
|
195
|
-
new_value = arg_hash[
|
|
196
|
-
existing_value = instance.send(
|
|
244
|
+
supertype.identifying_roles.each do |role|
|
|
245
|
+
next unless arg_hash.include?(role.name) # No contradiction here
|
|
246
|
+
new_value = arg_hash[role.name]
|
|
247
|
+
existing_value = instance.send(role.name.to_sym)
|
|
197
248
|
|
|
198
249
|
# Quick check for an exact match:
|
|
199
|
-
|
|
250
|
+
counterpart_class = role.counterpart && role.counterpart.object_type
|
|
251
|
+
next if existing_value == new_value or existing_value.identifying_role_values(counterpart_class) == new_value
|
|
200
252
|
|
|
201
253
|
# Coerce the new value to identifying values for the counterpart role's type:
|
|
202
|
-
role = supertype.roles(
|
|
254
|
+
role = supertype.roles(role.name)
|
|
203
255
|
new_key = role.counterpart.object_type.identifying_role_values(instance.constellation, [new_value])
|
|
204
256
|
# REVISIT: Check that the next line actually gets hit, otherwise strip it out
|
|
205
257
|
next if existing_value == new_key # This can happen when the counterpart is a value type
|
|
206
258
|
|
|
207
|
-
existing_key = existing_value.identifying_role_values
|
|
259
|
+
existing_key = existing_value.identifying_role_values(counterpart_class)
|
|
208
260
|
next if existing_key == new_key
|
|
209
261
|
raise TypeConflictException.new(basename, supertype, new_key, existing_key)
|
|
210
262
|
end
|
|
@@ -248,7 +300,7 @@ module ActiveFacts
|
|
|
248
300
|
|
|
249
301
|
# Complain if we have left-over arguments
|
|
250
302
|
if args.size > 0
|
|
251
|
-
raise
|
|
303
|
+
raise UnexpectedIdentifyingValueException.new(self, irns, args)
|
|
252
304
|
end
|
|
253
305
|
|
|
254
306
|
# The arg_hash will be used to construct a new instance, if necessary
|
|
@@ -266,7 +318,8 @@ module ActiveFacts
|
|
|
266
318
|
end
|
|
267
319
|
elsif proto
|
|
268
320
|
value = proto.send(n)
|
|
269
|
-
|
|
321
|
+
counterpart_class = role.counterpart && role.counterpart.object_type
|
|
322
|
+
value = value.identifying_role_values(counterpart_class)
|
|
270
323
|
arg_hash[n] = value # Save the value for making a new instance
|
|
271
324
|
next value if (role.is_unary)
|
|
272
325
|
else
|
|
@@ -338,7 +391,7 @@ module ActiveFacts
|
|
|
338
391
|
# which is a list of roles it plays. The identification scheme may be
|
|
339
392
|
# inherited from a superclass.
|
|
340
393
|
def identified_by(*args) #:nodoc:
|
|
341
|
-
raise
|
|
394
|
+
raise MissingIdentificationException.new(self) unless args.size > 0
|
|
342
395
|
|
|
343
396
|
# Catch the case where we state the same identification as our superclass:
|
|
344
397
|
inherited_role_names = identifying_role_names
|
|
@@ -9,31 +9,79 @@ module ActiveFacts
|
|
|
9
9
|
class ActiveFactsException < StandardError
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
class
|
|
12
|
+
class SchemaException < ActiveFactsException
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
class
|
|
15
|
+
class RuntimeException < ActiveFactsException
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
class CrossVocabularyRoleException <
|
|
18
|
+
class CrossVocabularyRoleException < SchemaException
|
|
19
19
|
def initialize klass, vocabulary
|
|
20
20
|
super "#{klass} must be an object type in #{vocabulary.name}"
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
class
|
|
24
|
+
class InvalidEntityException < SchemaException
|
|
25
|
+
def initialize klass
|
|
26
|
+
super "#{klass.basename} may not be an entity type"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class InvalidIdentificationException < SchemaException
|
|
31
|
+
def initialize object_type, role, is_single
|
|
32
|
+
msg =
|
|
33
|
+
if is_single
|
|
34
|
+
"#{object_type} has a single identifying role '#{role}' which is has_one, but must be one_to_one"
|
|
35
|
+
else
|
|
36
|
+
raise "#{object_type} has an identifying role '#{role}' which is one_to_one, but must be has_one"
|
|
37
|
+
end
|
|
38
|
+
super msg
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class MissingIdentificationException < SchemaException
|
|
43
|
+
def initialize klass
|
|
44
|
+
super "You must list the roles which will identify #{klass.basename}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class InvalidObjectType < SchemaException
|
|
49
|
+
def initialize vocabulary, klass, reason
|
|
50
|
+
raise "A constellation over #{vocabulary.name} cannot index instances of #{klass} because it #{reason}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class InvalidSupertypeException < SchemaException
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class DuplicateRoleException < SchemaException
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class UnrecognisedOptionsException < SchemaException
|
|
61
|
+
def initialize declaration, instance, option_names
|
|
62
|
+
raise "Unrecognised options on declaration of #{declaration} #{instance}: #{option_names.inspect}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class RoleNotDefinedException < RuntimeException
|
|
25
67
|
def initialize klass, role_name
|
|
26
68
|
super "Role #{klass.basename}.#{role_name} is not defined"
|
|
27
69
|
end
|
|
28
70
|
end
|
|
29
71
|
|
|
30
|
-
class
|
|
72
|
+
class UnexpectedIdentifyingValueException < RuntimeException
|
|
73
|
+
def initialize object_type, identifying_role_names, extra_args
|
|
74
|
+
super "#{object_type.basename} expects only (#{identifying_role_names*', '}) for its identifier, but you provided additional values #{extra_args.inspect}"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class MissingMandatoryRoleValueException < RuntimeException
|
|
31
79
|
def initialize klass, role
|
|
32
80
|
super "A #{role.counterpart.object_type.basename} is required to satisfy the #{role.name.inspect} role of #{klass.basename}"
|
|
33
81
|
end
|
|
34
82
|
end
|
|
35
83
|
|
|
36
|
-
class DuplicateIdentifyingValueException <
|
|
84
|
+
class DuplicateIdentifyingValueException < RuntimeException
|
|
37
85
|
def initialize(klass, role_name, value)
|
|
38
86
|
super("Illegal attempt to assert #{klass.basename} having identifying value" +
|
|
39
87
|
" (#{role_name} is #{value.verbalise})," +
|
|
@@ -42,14 +90,14 @@ module ActiveFacts
|
|
|
42
90
|
end
|
|
43
91
|
|
|
44
92
|
# When an existing object having multiple identification patterns is re-asserted, all the keys must match the existing object
|
|
45
|
-
class TypeConflictException <
|
|
93
|
+
class TypeConflictException < RuntimeException
|
|
46
94
|
def initialize(klass, supertype, key, existing)
|
|
47
95
|
super "#{klass} cannot be asserted to have #{supertype} identifier #{key.inspect} because the existing object has #{existing.inspect}"
|
|
48
96
|
end
|
|
49
97
|
end
|
|
50
98
|
|
|
51
99
|
# When a new entity is asserted, but a supertype identifier matches an existing object of a different type, type migration is implied but unfortunately is impossible in Ruby
|
|
52
|
-
class TypeMigrationException <
|
|
100
|
+
class TypeMigrationException < RuntimeException
|
|
53
101
|
def initialize(klass, supertype, key)
|
|
54
102
|
super "#{klass} cannot be asserted due to the prior existence of a conflicting #{supertype} identified by #{key.inspect}"
|
|
55
103
|
end
|
data/lib/activefacts/api/guid.rb
CHANGED
|
@@ -16,7 +16,7 @@ class Guid
|
|
|
16
16
|
elsif (v = i.to_s).length == 36 and !(v !~ /[^0-9a-f]/i)
|
|
17
17
|
@value = v.clone.freeze
|
|
18
18
|
else
|
|
19
|
-
raise "Illegal non-Guid value #{i.inspect} given for Guid"
|
|
19
|
+
raise ArgumentError.new("Illegal non-Guid value #{i.inspect} given for Guid")
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -34,19 +34,20 @@ module ActiveFacts
|
|
|
34
34
|
|
|
35
35
|
# If this instance's role is updated to the new value, does that cause a collision?
|
|
36
36
|
# We need to check each superclass that has a different identification pattern
|
|
37
|
-
def
|
|
37
|
+
def check_identification_change_legality(role, value)
|
|
38
38
|
return unless @constellation && role.is_identifying
|
|
39
39
|
|
|
40
40
|
klasses = [self.class] + self.class.supertypes_transitive
|
|
41
41
|
last_identity = nil
|
|
42
42
|
last_irns = nil
|
|
43
|
+
counterpart_class = role.counterpart ? role.counterpart.object_type : value.class
|
|
43
44
|
duplicate = klasses.detect do |klass|
|
|
44
45
|
next false unless klass.identifying_roles.include?(role)
|
|
45
46
|
irns = klass.identifying_role_names
|
|
46
47
|
if last_irns != irns
|
|
47
48
|
last_identity = identifying_role_values(klass)
|
|
48
49
|
role_position = irns.index(role.name)
|
|
49
|
-
last_identity[role_position] = value.identifying_role_values
|
|
50
|
+
last_identity[role_position] = value.identifying_role_values(counterpart_class)
|
|
50
51
|
end
|
|
51
52
|
@constellation.instances[klass][last_identity]
|
|
52
53
|
end
|
|
@@ -90,11 +91,19 @@ module ActiveFacts
|
|
|
90
91
|
# Objects being created do not have to have non-identifying mandatory roles,
|
|
91
92
|
# so we allow retracting to the same state.
|
|
92
93
|
if role.unique
|
|
94
|
+
i = send(role.getter)
|
|
95
|
+
next unless i
|
|
93
96
|
if counterpart.is_identifying && counterpart.mandatory
|
|
94
|
-
i
|
|
97
|
+
i.retract
|
|
95
98
|
else
|
|
96
|
-
|
|
99
|
+
if (counterpart.unique)
|
|
100
|
+
# REVISIT: This will incorrectly fail to propagate a key change for a non-mandatory role
|
|
101
|
+
i.send(counterpart.setter, nil, false)
|
|
102
|
+
else
|
|
103
|
+
i.send(role.counterpart.getter).update(self, nil)
|
|
104
|
+
end
|
|
97
105
|
end
|
|
106
|
+
instance_variable_set(role.variable, nil)
|
|
98
107
|
else
|
|
99
108
|
# puts "Not removing role #{role_name} from counterpart RoleValues #{counterpart.name}"
|
|
100
109
|
# Duplicate the array using to_a, as the RoleValues here will be modified as we traverse it:
|
|
@@ -104,9 +113,10 @@ module ActiveFacts
|
|
|
104
113
|
if counterpart.is_identifying && counterpart.mandatory
|
|
105
114
|
counterpart_instance.retract
|
|
106
115
|
else
|
|
107
|
-
counterpart_instance.send(counterpart.setter, nil)
|
|
116
|
+
counterpart_instance.send(counterpart.setter, nil, false)
|
|
108
117
|
end
|
|
109
118
|
end
|
|
119
|
+
instance_variable_set(role.variable, nil)
|
|
110
120
|
end
|
|
111
121
|
end
|
|
112
122
|
end
|
|
@@ -116,7 +116,7 @@ class AutoCounter
|
|
|
116
116
|
attr_reader :place_holder_number
|
|
117
117
|
def initialize(i = :new)
|
|
118
118
|
unless i == :new or i.is_a?(Integer) or i.is_a?(AutoCounter)
|
|
119
|
-
raise "AutoCounter #{self.class} may not be #{i.inspect}"
|
|
119
|
+
raise ArgumentError.new("AutoCounter #{self.class} may not be #{i.inspect}")
|
|
120
120
|
end
|
|
121
121
|
@@place_holder ||= 0
|
|
122
122
|
case i
|
|
@@ -191,7 +191,7 @@ class AutoCounter
|
|
|
191
191
|
to_s.eql?(o.to_s)
|
|
192
192
|
end
|
|
193
193
|
|
|
194
|
-
def identifying_role_values
|
|
194
|
+
def identifying_role_values klass = nil
|
|
195
195
|
self
|
|
196
196
|
end
|
|
197
197
|
|
|
@@ -46,6 +46,15 @@ module ActiveFacts
|
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
def all_roles
|
|
50
|
+
return @all_roles if @all_roles
|
|
51
|
+
@all_roles = roles.dup
|
|
52
|
+
supertypes_transitive.each do |klass|
|
|
53
|
+
@all_roles.merge!(klass.roles)
|
|
54
|
+
end
|
|
55
|
+
@all_roles
|
|
56
|
+
end
|
|
57
|
+
|
|
49
58
|
# Define a unary fact type attached to this object_type; in essence, a boolean attribute.
|
|
50
59
|
#
|
|
51
60
|
# Example: maybe :is_ceo
|
|
@@ -91,11 +100,11 @@ module ActiveFacts
|
|
|
91
100
|
case type
|
|
92
101
|
when :has_one
|
|
93
102
|
if identifying_role_names.size == 1
|
|
94
|
-
|
|
103
|
+
raise InvalidIdentificationException.new(self, role, true)
|
|
95
104
|
end
|
|
96
105
|
when :one_to_one
|
|
97
106
|
if identifying_role_names.size > 1
|
|
98
|
-
|
|
107
|
+
raise InvalidIdentificationException.new(self, role, false)
|
|
99
108
|
end
|
|
100
109
|
end
|
|
101
110
|
end
|
|
@@ -120,12 +129,14 @@ module ActiveFacts
|
|
|
120
129
|
# No late binding here:
|
|
121
130
|
(object_type = vocabulary.const_get(object_type.to_s.camelcase))
|
|
122
131
|
else
|
|
123
|
-
raise "Illegal supertype #{object_type.inspect} for #{self.class.basename}"
|
|
132
|
+
raise InvalidSupertypeException.new("Illegal supertype #{object_type.inspect} for #{self.class.basename}")
|
|
124
133
|
end
|
|
125
|
-
|
|
134
|
+
unless supertype.respond_to?(:vocabulary) and supertype.vocabulary == self.vocabulary
|
|
135
|
+
raise InvalidSupertypeException.new("#{supertype.name} must be an object type in #{vocabulary.name}")
|
|
136
|
+
end
|
|
126
137
|
|
|
127
138
|
if is_entity_type != supertype.is_entity_type
|
|
128
|
-
raise "#{self} < #{supertype}: A value type may not be a supertype of an entity type, and vice versa"
|
|
139
|
+
raise InvalidSupertypeException.new("#{self} < #{supertype}: A value type may not be a supertype of an entity type, and vice versa")
|
|
129
140
|
end
|
|
130
141
|
|
|
131
142
|
@supertypes << supertype
|
|
@@ -194,8 +205,11 @@ module ActiveFacts
|
|
|
194
205
|
|
|
195
206
|
# Shared code for both kinds of binary fact type (has_one and one_to_one)
|
|
196
207
|
def define_binary_fact_type(one_to_one, role_name, related, mandatory, related_role_name)
|
|
197
|
-
|
|
198
|
-
|
|
208
|
+
# REVISIT: This should be all_roles, to catch duplicate role name in a supertype
|
|
209
|
+
# if all_roles[role_name]
|
|
210
|
+
if roles[role_name]
|
|
211
|
+
raise DuplicateRoleException.new("#{name} cannot have more than one role named #{role_name}")
|
|
212
|
+
end
|
|
199
213
|
roles[role_name] = role = Role.new(self, nil, role_name, mandatory)
|
|
200
214
|
|
|
201
215
|
# There may be a forward reference here where role_name is a Symbol,
|
|
@@ -228,7 +242,9 @@ module ActiveFacts
|
|
|
228
242
|
|
|
229
243
|
def define_single_role_getter(role)
|
|
230
244
|
define_method role.getter do |*a|
|
|
231
|
-
|
|
245
|
+
if a.size > 0
|
|
246
|
+
raise ArgumentError.new("wrong number of arguments (#{a.size} for 0)")
|
|
247
|
+
end
|
|
232
248
|
instance_variable_get(role.variable)
|
|
233
249
|
end
|
|
234
250
|
end
|
|
@@ -236,46 +252,40 @@ module ActiveFacts
|
|
|
236
252
|
def define_one_to_one_accessor(role)
|
|
237
253
|
define_single_role_getter(role)
|
|
238
254
|
|
|
239
|
-
|
|
240
|
-
|
|
255
|
+
# What I want is the following, but it doesn't work in Ruby 1.8
|
|
256
|
+
# define_method role.setter do |value, mutual_propagation = true|
|
|
257
|
+
define_method role.setter do |*a|
|
|
258
|
+
value, mutual_propagation = *a
|
|
259
|
+
mutual_propagation = true if a.size < 2
|
|
260
|
+
role_var = role.variable
|
|
241
261
|
|
|
242
|
-
#
|
|
243
|
-
|
|
262
|
+
# Get old value, and jump out early if it's unchanged:
|
|
263
|
+
old = instance_variable_get(role_var)
|
|
264
|
+
return value if old == value
|
|
244
265
|
|
|
266
|
+
# assert a new instance for the role value if necessary
|
|
245
267
|
if value and o = role.counterpart.object_type and (!value.is_a?(o) || value.constellation != @constellation)
|
|
246
268
|
value = @constellation.assert(o, *Array(value))
|
|
247
|
-
|
|
248
|
-
return true if old == value
|
|
269
|
+
return value if old == value
|
|
249
270
|
end
|
|
250
271
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
check_value_change_legality(role, value)
|
|
272
|
+
if (role.is_identifying) # We're changing this object's key. Check legality and prepare to propagate
|
|
273
|
+
check_identification_change_legality(role, value)
|
|
254
274
|
|
|
255
|
-
#
|
|
256
|
-
|
|
257
|
-
if @constellation && old
|
|
258
|
-
dependent_entities = old.related_entities.map do |entity|
|
|
259
|
-
[entity.identifying_role_values, entity]
|
|
260
|
-
end
|
|
261
|
-
end
|
|
275
|
+
# puts "Starting to analyse impact of changing 1-1 #{role.inspect} to #{value.inspect}"
|
|
276
|
+
impacts = analyse_impacts(role)
|
|
262
277
|
end
|
|
263
278
|
|
|
264
|
-
instance_variable_set(
|
|
279
|
+
instance_variable_set(role_var, value)
|
|
265
280
|
|
|
266
281
|
# Remove self from the old counterpart:
|
|
267
|
-
old.send(role.counterpart.setter, nil) if old
|
|
282
|
+
old.send(role.counterpart.setter, nil, false) if old and mutual_propagation
|
|
268
283
|
|
|
269
284
|
@constellation.when_admitted do
|
|
270
285
|
# Assign self to the new counterpart
|
|
271
286
|
value.send(role.counterpart.setter, self) if value
|
|
272
287
|
|
|
273
|
-
# Propagate dependent key changes
|
|
274
|
-
if dependent_entities
|
|
275
|
-
dependent_entities.each do |old_key, entity|
|
|
276
|
-
entity.instance_index.refresh_key(old_key)
|
|
277
|
-
end
|
|
278
|
-
end
|
|
288
|
+
apply_impacts(impacts) if impacts # Propagate dependent key changes
|
|
279
289
|
end
|
|
280
290
|
|
|
281
291
|
value
|
|
@@ -285,46 +295,40 @@ module ActiveFacts
|
|
|
285
295
|
def define_one_to_many_accessor(role)
|
|
286
296
|
define_single_role_getter(role)
|
|
287
297
|
|
|
288
|
-
|
|
298
|
+
# What I want is the following, but it doesn't work in Ruby 1.8
|
|
299
|
+
# define_method role.setter do |value, mutual_propagation = true|
|
|
300
|
+
define_method role.setter do |*a|
|
|
301
|
+
value, mutual_propagation = *a
|
|
302
|
+
mutual_propagation = true if a.size < 2
|
|
289
303
|
role_var = role.variable
|
|
290
304
|
|
|
291
305
|
# Get old value, and jump out early if it's unchanged:
|
|
292
306
|
old = instance_variable_get(role_var)
|
|
293
|
-
return value if old
|
|
307
|
+
return value if old == value
|
|
294
308
|
|
|
295
309
|
# assert a new instance for the role value if necessary
|
|
296
310
|
if value and o = role.counterpart.object_type and (!value.is_a?(o) || value.constellation != @constellation)
|
|
297
311
|
value = @constellation.assert(o, *Array(value))
|
|
298
|
-
return value if old
|
|
312
|
+
return value if old == value # Occurs when another instance having the same value is assigned
|
|
299
313
|
end
|
|
300
314
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
check_value_change_legality(role, value) if value
|
|
315
|
+
if (role.is_identifying) # We're changing this object's key. Check legality and prepare to propagate
|
|
316
|
+
check_identification_change_legality(role, value)
|
|
304
317
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
dependent_entities = old.related_entities.map do |entity|
|
|
308
|
-
[entity.identifying_role_values, entity]
|
|
309
|
-
end
|
|
310
|
-
end
|
|
318
|
+
# puts "Starting to analyse impact of changing 1-N #{role.inspect} to #{value.inspect}"
|
|
319
|
+
impacts = analyse_impacts(role)
|
|
311
320
|
end
|
|
312
321
|
|
|
313
322
|
instance_variable_set(role_var, value)
|
|
314
323
|
|
|
315
324
|
# Remove "self" from the old counterpart:
|
|
316
|
-
old.send(getter = role.counterpart.getter).update(self, nil) if old
|
|
325
|
+
old.send(getter = role.counterpart.getter).update(self, nil) if old && mutual_propagation
|
|
317
326
|
|
|
318
327
|
@constellation.when_admitted do
|
|
319
|
-
# REVISIT: Delay co-referencing here if the object is still a candidate
|
|
320
328
|
# Add "self" into the counterpart
|
|
321
329
|
value.send(getter ||= role.counterpart.getter).update(old, self) if value
|
|
322
330
|
|
|
323
|
-
if
|
|
324
|
-
dependent_entities.each do |key, entity|
|
|
325
|
-
entity.instance_index.refresh_key(key)
|
|
326
|
-
end
|
|
327
|
-
end
|
|
331
|
+
apply_impacts(impacts) if impacts # Propagate dependent key changes
|
|
328
332
|
end
|
|
329
333
|
|
|
330
334
|
value
|
|
@@ -391,7 +395,7 @@ module ActiveFacts
|
|
|
391
395
|
related = related_name
|
|
392
396
|
related_name = related_name.to_s.snakecase
|
|
393
397
|
else
|
|
394
|
-
raise "Invalid type for :class option on :#{role_name}"
|
|
398
|
+
raise ArgumentError.new("Invalid type #{related_name.class} for :class option on :#{role_name}, must be a Class, Symbol or String")
|
|
395
399
|
end
|
|
396
400
|
|
|
397
401
|
# resolve the Symbol to a Class now if possible:
|
|
@@ -412,11 +416,15 @@ module ActiveFacts
|
|
|
412
416
|
reading = options.delete(:reading) # REVISIT: Implement verbalisation
|
|
413
417
|
role_value_constraint = options.delete(:restrict) # REVISIT: Implement role value constraints
|
|
414
418
|
|
|
415
|
-
|
|
419
|
+
additional_role_options options
|
|
420
|
+
|
|
421
|
+
raise UnrecognisedOptionsException.new("role", role_name, options.keys) unless options.empty?
|
|
416
422
|
|
|
417
|
-
#
|
|
418
|
-
|
|
419
|
-
|
|
423
|
+
# If you have a role "supervisor" and a sub-class "Supervisor", this'll bitch.
|
|
424
|
+
if !specified_class and # No specified :class was provided
|
|
425
|
+
related.is_a?(Class) and
|
|
426
|
+
(indicated = vocabulary.object_type(role_name)) and
|
|
427
|
+
indicated != related
|
|
420
428
|
raise "Role name #{role_name} indicates a different counterpart object_type #{indicated} than specified"
|
|
421
429
|
end
|
|
422
430
|
|
|
@@ -439,6 +447,10 @@ module ActiveFacts
|
|
|
439
447
|
]
|
|
440
448
|
end
|
|
441
449
|
|
|
450
|
+
def additional_role_options options
|
|
451
|
+
# This is a hook for extensions to override. Any extension options should be deleted from the options hash.
|
|
452
|
+
end
|
|
453
|
+
|
|
442
454
|
def when_bound(object_type, *args, &block)
|
|
443
455
|
case object_type
|
|
444
456
|
when Class
|
|
@@ -33,19 +33,25 @@ ValueClasses.each{|c|
|
|
|
33
33
|
# Cannot subclass or delegate True, False or nil, so inject the required behaviour
|
|
34
34
|
class TrueClass #:nodoc:
|
|
35
35
|
def verbalise(role_name = nil); role_name ? "#{role_name}: true" : "true"; end
|
|
36
|
-
def identifying_role_values
|
|
36
|
+
def identifying_role_values klass = nil
|
|
37
|
+
self
|
|
38
|
+
end
|
|
37
39
|
def self.identifying_role_values(*a); a.replace([{}]); true end
|
|
38
40
|
end
|
|
39
41
|
|
|
40
42
|
class FalseClass #:nodoc:
|
|
41
43
|
def verbalise(role_name = nil); role_name ? "#{role_name}: false" : "false"; end
|
|
42
|
-
def identifying_role_values
|
|
44
|
+
def identifying_role_values klass = nil
|
|
45
|
+
self
|
|
46
|
+
end
|
|
43
47
|
def self.identifying_role_values(*a); a.replace([{}]); false end
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
class NilClass #:nodoc:
|
|
47
51
|
def verbalise; "nil"; end
|
|
48
|
-
def identifying_role_values
|
|
52
|
+
def identifying_role_values klass = nil
|
|
53
|
+
self
|
|
54
|
+
end
|
|
49
55
|
def self.identifying_role_values(*a); a.replace([{}]); nil end
|
|
50
56
|
end
|
|
51
57
|
|
|
@@ -53,7 +59,7 @@ class Class
|
|
|
53
59
|
# Make this Class into a ObjectType and if necessary its module into a Vocabulary.
|
|
54
60
|
# The parameters are the names (Symbols) of the identifying roles.
|
|
55
61
|
def identified_by *args, &b
|
|
56
|
-
raise
|
|
62
|
+
raise InvalidEntityException(self) if respond_to? :value_type # Don't make a ValueType into an EntityType
|
|
57
63
|
|
|
58
64
|
# The inclusion of instance methods triggers ClassMethods to be included in the class too
|
|
59
65
|
include ActiveFacts::API::Entity
|
|
@@ -28,9 +28,8 @@ module ActiveFacts
|
|
|
28
28
|
|
|
29
29
|
# A value is its own key, unless it's a delegate for a raw value
|
|
30
30
|
def identifying_role_values(klass = nil) #:nodoc:
|
|
31
|
-
# The identifying role value for the supertype of a value type is always the same as for the subtype
|
|
32
|
-
|
|
33
|
-
__getobj__ rescue self
|
|
31
|
+
# The identifying role value for the supertype of a value type is always the same as for the subtype
|
|
32
|
+
respond_to?(:__getobj__) ? __getobj__ : self
|
|
34
33
|
end
|
|
35
34
|
|
|
36
35
|
# All ValueType classes include the methods defined here
|
|
@@ -41,7 +40,7 @@ module ActiveFacts
|
|
|
41
40
|
# REVISIT: args could be a hash, with keys :length, :scale, :unit, :allow
|
|
42
41
|
options = (args[-1].is_a?(Hash) ? args.pop : {})
|
|
43
42
|
options.each do |key, value|
|
|
44
|
-
|
|
43
|
+
raise UnrecognisedOptionsException.new('value_type', basename, key) unless respond_to?(key)
|
|
45
44
|
send(key, value)
|
|
46
45
|
end
|
|
47
46
|
end
|
|
@@ -84,7 +83,7 @@ module ActiveFacts
|
|
|
84
83
|
instance = new_instance(constellation, *args)
|
|
85
84
|
end
|
|
86
85
|
args.replace([arg_hash])
|
|
87
|
-
instance.identifying_role_values
|
|
86
|
+
instance.identifying_role_values(self)
|
|
88
87
|
end
|
|
89
88
|
|
|
90
89
|
def assert_instance(constellation, args)
|
|
@@ -116,7 +115,7 @@ module ActiveFacts
|
|
|
116
115
|
def index_instance(constellation, instance) #:nodoc:
|
|
117
116
|
# Index the instance in the constellation's InstanceIndex for this class:
|
|
118
117
|
instances = constellation.instances[self]
|
|
119
|
-
key = instance.identifying_role_values
|
|
118
|
+
key = instance.identifying_role_values(self)
|
|
120
119
|
instances[key] = instance
|
|
121
120
|
|
|
122
121
|
# Index the instance for each supertype:
|
|
@@ -32,11 +32,15 @@ module ActiveFacts
|
|
|
32
32
|
__bind(camel)
|
|
33
33
|
c
|
|
34
34
|
else
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
if defined?(camel)
|
|
36
|
+
begin
|
|
37
|
+
const_get(camel)
|
|
38
|
+
rescue NameError
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
40
44
|
end
|
|
41
45
|
end
|
|
42
46
|
|
|
@@ -84,8 +84,8 @@ describe "A Constellation instance" do
|
|
|
84
84
|
|
|
85
85
|
it "should complain when accessing a class that isn't an object type" do
|
|
86
86
|
class Mod::Bar; end
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
proc { @constellation.Bar }.should raise_error
|
|
88
|
+
proc { @constellation.instances[Mod::Bar] }.should raise_error
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
it "should deny handling an object type defined outside the current module" do
|
|
@@ -200,7 +200,7 @@ describe "A Constellation instance" do
|
|
|
200
200
|
|
|
201
201
|
it "Should raise an exception with assigning a role whose referent (object type) has not yet been defined" do
|
|
202
202
|
n = @constellation.Name("Fred")
|
|
203
|
-
# This does
|
|
203
|
+
# This does not raise the "settable_roles_exception". I'm no longer sure how I did this, so I can't get coverage on this code :(
|
|
204
204
|
proc { n.undefined_role = 'foo' }.should raise_error
|
|
205
205
|
end
|
|
206
206
|
|
|
@@ -447,7 +447,7 @@ describe "A Constellation instance" do
|
|
|
447
447
|
supertypes Bignum
|
|
448
448
|
end
|
|
449
449
|
end
|
|
450
|
-
}.should raise_error(
|
|
450
|
+
}.should raise_error(ActiveFacts::API::InvalidSupertypeException)
|
|
451
451
|
|
|
452
452
|
lambda {
|
|
453
453
|
module Mod
|
|
@@ -455,7 +455,7 @@ describe "A Constellation instance" do
|
|
|
455
455
|
supertypes 3
|
|
456
456
|
end
|
|
457
457
|
end
|
|
458
|
-
}.should raise_error(
|
|
458
|
+
}.should raise_error(ActiveFacts::API::InvalidSupertypeException)
|
|
459
459
|
end
|
|
460
460
|
|
|
461
461
|
it "should allow supertypes with supertypes" do
|
|
@@ -160,7 +160,7 @@ describe "Object type role values" do
|
|
|
160
160
|
instances.map{|k,o| o}.first.should == object
|
|
161
161
|
unless object.class.is_entity_type
|
|
162
162
|
# Look up value types using the value instance, not just the raw value:
|
|
163
|
-
instances[object].should == object
|
|
163
|
+
instances[object.identifying_role_values].should == object
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
# Make sure all the identifying roles are populated correctly:
|
|
@@ -254,7 +254,7 @@ describe "An Entity Type" do
|
|
|
254
254
|
|
|
255
255
|
it "should assert the identifying values" do
|
|
256
256
|
@rn.should be_a(Mod::Number)
|
|
257
|
-
@c.Number[@rn].should == @rn # Yes
|
|
257
|
+
@c.Number[@rn.identifying_role_values].should == @rn # Yes
|
|
258
258
|
@c.Number[101].should == @rn # No
|
|
259
259
|
@c.Number[101].should be_eql 101 # No
|
|
260
260
|
end
|
data/spec/metadata_spec.rb
CHANGED
|
@@ -15,6 +15,7 @@ describe "In a vocabulary" do
|
|
|
15
15
|
ObjectType_methods = [
|
|
16
16
|
:has_one, :maybe, :one_to_one,
|
|
17
17
|
:roles, :subtypes, :supertypes, :vocabulary,
|
|
18
|
+
:all_roles,
|
|
18
19
|
# To make private:
|
|
19
20
|
:check_identifying_role_has_valid_cardinality, :realise_role, :supertypes_transitive,
|
|
20
21
|
]
|
|
@@ -27,7 +28,7 @@ describe "In a vocabulary" do
|
|
|
27
28
|
Instance_methods = [
|
|
28
29
|
:constellation, :retract, :is_a?,
|
|
29
30
|
# To remove or move to EntityType
|
|
30
|
-
:related_entities, :
|
|
31
|
+
:related_entities, :check_identification_change_legality,
|
|
31
32
|
:instance_index
|
|
32
33
|
]
|
|
33
34
|
Value_methods = Instance_methods + [
|
data/spec/spec_helper.rb
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
if ENV["RSPEC_REPORT_EXCEPTIONS"]
|
|
2
|
+
class Exception
|
|
3
|
+
alias_method :old_initialize, :initialize
|
|
4
|
+
|
|
5
|
+
def self.exceptions_seen
|
|
6
|
+
@@seen ||= {}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.see args
|
|
10
|
+
stack = caller[1..-1]
|
|
11
|
+
# Don't show expected exceptions or RSpec internal ones
|
|
12
|
+
return if caller.detect{|s| s =~ %r{rspec/matchers/raise_error}} or
|
|
13
|
+
self.name =~ /^RSpec::/
|
|
14
|
+
stack.reject!{|s| s =~ %r{/rspec/}}
|
|
15
|
+
key = stack[0,4]+[self.name]
|
|
16
|
+
return if exceptions_seen[key]
|
|
17
|
+
|
|
18
|
+
exceptions_seen[key] = show = stack
|
|
19
|
+
args[0] = args[0].to_str if args[0].class.name == "NameError::message"
|
|
20
|
+
puts "#{self.name}#{args.inspect}:\n\t#{show*"\n\t"}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize *args, &b
|
|
24
|
+
self.class.see args
|
|
25
|
+
|
|
26
|
+
send(:old_initialize, *args, &b)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
metadata
CHANGED
|
@@ -1,199 +1,190 @@
|
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activefacts-api
|
|
3
|
-
version: !ruby/object:Gem::Version
|
|
4
|
-
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 55
|
|
5
5
|
prerelease:
|
|
6
|
+
segments:
|
|
7
|
+
- 0
|
|
8
|
+
- 9
|
|
9
|
+
- 6
|
|
10
|
+
version: 0.9.6
|
|
6
11
|
platform: ruby
|
|
7
|
-
authors:
|
|
12
|
+
authors:
|
|
8
13
|
- Clifford Heath
|
|
9
14
|
autorequire:
|
|
10
15
|
bindir: bin
|
|
11
16
|
cert_chain: []
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
|
|
18
|
+
date: 2013-01-22 00:00:00 Z
|
|
19
|
+
dependencies:
|
|
20
|
+
- !ruby/object:Gem::Dependency
|
|
15
21
|
name: rake
|
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
|
17
|
-
none: false
|
|
18
|
-
requirements:
|
|
19
|
-
- - ! '>='
|
|
20
|
-
- !ruby/object:Gem::Version
|
|
21
|
-
version: '0'
|
|
22
|
-
type: :development
|
|
23
22
|
prerelease: false
|
|
24
|
-
|
|
23
|
+
type: :development
|
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
25
25
|
none: false
|
|
26
|
-
requirements:
|
|
27
|
-
- -
|
|
28
|
-
- !ruby/object:Gem::Version
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
hash: 3
|
|
30
|
+
segments:
|
|
31
|
+
- 0
|
|
32
|
+
version: "0"
|
|
33
|
+
version_requirements: *id001
|
|
34
|
+
- !ruby/object:Gem::Dependency
|
|
31
35
|
name: jeweler
|
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
|
33
|
-
none: false
|
|
34
|
-
requirements:
|
|
35
|
-
- - ! '>='
|
|
36
|
-
- !ruby/object:Gem::Version
|
|
37
|
-
version: '0'
|
|
38
|
-
type: :development
|
|
39
36
|
prerelease: false
|
|
40
|
-
|
|
37
|
+
type: :development
|
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
41
39
|
none: false
|
|
42
|
-
requirements:
|
|
43
|
-
- -
|
|
44
|
-
- !ruby/object:Gem::Version
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
requirements:
|
|
41
|
+
- - ">="
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
hash: 3
|
|
44
|
+
segments:
|
|
45
|
+
- 0
|
|
46
|
+
version: "0"
|
|
47
|
+
version_requirements: *id002
|
|
48
|
+
- !ruby/object:Gem::Dependency
|
|
47
49
|
name: rspec
|
|
48
|
-
requirement: !ruby/object:Gem::Requirement
|
|
49
|
-
none: false
|
|
50
|
-
requirements:
|
|
51
|
-
- - ~>
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: 2.6.0
|
|
54
|
-
type: :development
|
|
55
50
|
prerelease: false
|
|
56
|
-
|
|
51
|
+
type: :development
|
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
57
53
|
none: false
|
|
58
|
-
requirements:
|
|
54
|
+
requirements:
|
|
59
55
|
- - ~>
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
hash: 23
|
|
58
|
+
segments:
|
|
59
|
+
- 2
|
|
60
|
+
- 6
|
|
61
|
+
- 0
|
|
61
62
|
version: 2.6.0
|
|
62
|
-
|
|
63
|
+
version_requirements: *id003
|
|
64
|
+
- !ruby/object:Gem::Dependency
|
|
63
65
|
name: ruby-debug
|
|
64
|
-
requirement: !ruby/object:Gem::Requirement
|
|
65
|
-
none: false
|
|
66
|
-
requirements:
|
|
67
|
-
- - ! '>='
|
|
68
|
-
- !ruby/object:Gem::Version
|
|
69
|
-
version: '0'
|
|
70
|
-
type: :development
|
|
71
66
|
prerelease: false
|
|
72
|
-
|
|
67
|
+
type: :development
|
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
|
73
69
|
none: false
|
|
74
|
-
requirements:
|
|
75
|
-
- -
|
|
76
|
-
- !ruby/object:Gem::Version
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
hash: 3
|
|
74
|
+
segments:
|
|
75
|
+
- 0
|
|
76
|
+
version: "0"
|
|
77
|
+
version_requirements: *id004
|
|
78
|
+
- !ruby/object:Gem::Dependency
|
|
79
79
|
name: debugger
|
|
80
|
-
requirement: !ruby/object:Gem::Requirement
|
|
81
|
-
none: false
|
|
82
|
-
requirements:
|
|
83
|
-
- - ! '>='
|
|
84
|
-
- !ruby/object:Gem::Version
|
|
85
|
-
version: '0'
|
|
86
|
-
type: :development
|
|
87
80
|
prerelease: false
|
|
88
|
-
|
|
81
|
+
type: :development
|
|
82
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
|
89
83
|
none: false
|
|
90
|
-
requirements:
|
|
91
|
-
- -
|
|
92
|
-
- !ruby/object:Gem::Version
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
hash: 3
|
|
88
|
+
segments:
|
|
89
|
+
- 0
|
|
90
|
+
version: "0"
|
|
91
|
+
version_requirements: *id005
|
|
92
|
+
- !ruby/object:Gem::Dependency
|
|
95
93
|
name: pry
|
|
96
|
-
requirement: !ruby/object:Gem::Requirement
|
|
97
|
-
none: false
|
|
98
|
-
requirements:
|
|
99
|
-
- - ! '>='
|
|
100
|
-
- !ruby/object:Gem::Version
|
|
101
|
-
version: '0'
|
|
102
|
-
type: :development
|
|
103
94
|
prerelease: false
|
|
104
|
-
|
|
95
|
+
type: :development
|
|
96
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
|
105
97
|
none: false
|
|
106
|
-
requirements:
|
|
107
|
-
- -
|
|
108
|
-
- !ruby/object:Gem::Version
|
|
109
|
-
|
|
110
|
-
|
|
98
|
+
requirements:
|
|
99
|
+
- - ">="
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
hash: 3
|
|
102
|
+
segments:
|
|
103
|
+
- 0
|
|
104
|
+
version: "0"
|
|
105
|
+
version_requirements: *id006
|
|
106
|
+
- !ruby/object:Gem::Dependency
|
|
111
107
|
name: rspec
|
|
112
|
-
requirement: !ruby/object:Gem::Requirement
|
|
113
|
-
none: false
|
|
114
|
-
requirements:
|
|
115
|
-
- - ~>
|
|
116
|
-
- !ruby/object:Gem::Version
|
|
117
|
-
version: 2.3.0
|
|
118
|
-
type: :development
|
|
119
108
|
prerelease: false
|
|
120
|
-
|
|
109
|
+
type: :development
|
|
110
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
|
121
111
|
none: false
|
|
122
|
-
requirements:
|
|
112
|
+
requirements:
|
|
123
113
|
- - ~>
|
|
124
|
-
- !ruby/object:Gem::Version
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
hash: 3
|
|
116
|
+
segments:
|
|
117
|
+
- 2
|
|
118
|
+
- 3
|
|
119
|
+
- 0
|
|
125
120
|
version: 2.3.0
|
|
126
|
-
|
|
121
|
+
version_requirements: *id007
|
|
122
|
+
- !ruby/object:Gem::Dependency
|
|
127
123
|
name: bundler
|
|
128
|
-
requirement: !ruby/object:Gem::Requirement
|
|
129
|
-
none: false
|
|
130
|
-
requirements:
|
|
131
|
-
- - ~>
|
|
132
|
-
- !ruby/object:Gem::Version
|
|
133
|
-
version: 1.0.0
|
|
134
|
-
type: :development
|
|
135
124
|
prerelease: false
|
|
136
|
-
|
|
125
|
+
type: :development
|
|
126
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
|
137
127
|
none: false
|
|
138
|
-
requirements:
|
|
128
|
+
requirements:
|
|
139
129
|
- - ~>
|
|
140
|
-
- !ruby/object:Gem::Version
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
hash: 23
|
|
132
|
+
segments:
|
|
133
|
+
- 1
|
|
134
|
+
- 0
|
|
135
|
+
- 0
|
|
141
136
|
version: 1.0.0
|
|
142
|
-
|
|
137
|
+
version_requirements: *id008
|
|
138
|
+
- !ruby/object:Gem::Dependency
|
|
143
139
|
name: jeweler
|
|
144
|
-
requirement: !ruby/object:Gem::Requirement
|
|
145
|
-
none: false
|
|
146
|
-
requirements:
|
|
147
|
-
- - ~>
|
|
148
|
-
- !ruby/object:Gem::Version
|
|
149
|
-
version: 1.5.2
|
|
150
|
-
type: :development
|
|
151
140
|
prerelease: false
|
|
152
|
-
|
|
141
|
+
type: :development
|
|
142
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
|
153
143
|
none: false
|
|
154
|
-
requirements:
|
|
144
|
+
requirements:
|
|
155
145
|
- - ~>
|
|
156
|
-
- !ruby/object:Gem::Version
|
|
146
|
+
- !ruby/object:Gem::Version
|
|
147
|
+
hash: 7
|
|
148
|
+
segments:
|
|
149
|
+
- 1
|
|
150
|
+
- 5
|
|
151
|
+
- 2
|
|
157
152
|
version: 1.5.2
|
|
158
|
-
|
|
153
|
+
version_requirements: *id009
|
|
154
|
+
- !ruby/object:Gem::Dependency
|
|
159
155
|
name: rdoc
|
|
160
|
-
requirement: !ruby/object:Gem::Requirement
|
|
161
|
-
none: false
|
|
162
|
-
requirements:
|
|
163
|
-
- - ! '>='
|
|
164
|
-
- !ruby/object:Gem::Version
|
|
165
|
-
version: 2.4.2
|
|
166
|
-
type: :development
|
|
167
156
|
prerelease: false
|
|
168
|
-
|
|
157
|
+
type: :development
|
|
158
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
|
169
159
|
none: false
|
|
170
|
-
requirements:
|
|
171
|
-
- -
|
|
172
|
-
- !ruby/object:Gem::Version
|
|
160
|
+
requirements:
|
|
161
|
+
- - ">="
|
|
162
|
+
- !ruby/object:Gem::Version
|
|
163
|
+
hash: 27
|
|
164
|
+
segments:
|
|
165
|
+
- 2
|
|
166
|
+
- 4
|
|
167
|
+
- 2
|
|
173
168
|
version: 2.4.2
|
|
174
|
-
|
|
175
|
-
|
|
169
|
+
version_requirements: *id010
|
|
170
|
+
description: |
|
|
171
|
+
|
|
176
172
|
The ActiveFacts API is a Ruby DSL for managing constellations of elementary facts.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
or
|
|
180
|
-
|
|
181
|
-
binary relational (A rel B). Relational facts are consistently co-referenced, so
|
|
182
|
-
you
|
|
183
|
-
|
|
173
|
+
Each fact is either existential (a value or an entity), characteristic (boolean) or
|
|
174
|
+
binary relational (A rel B). Relational facts are consistently co-referenced, so you
|
|
184
175
|
can traverse them efficiently in any direction. Each constellation maintains constraints
|
|
185
|
-
|
|
186
176
|
over the fact population.
|
|
187
177
|
|
|
188
|
-
'
|
|
189
178
|
email: clifford.heath@gmail.com
|
|
190
179
|
executables: []
|
|
180
|
+
|
|
191
181
|
extensions: []
|
|
192
|
-
|
|
182
|
+
|
|
183
|
+
extra_rdoc_files:
|
|
193
184
|
- LICENSE.txt
|
|
194
185
|
- README.rdoc
|
|
195
186
|
- TODO
|
|
196
|
-
files:
|
|
187
|
+
files:
|
|
197
188
|
- .document
|
|
198
189
|
- .rspec
|
|
199
190
|
- .travis.yml
|
|
@@ -239,33 +230,40 @@ files:
|
|
|
239
230
|
- spec/object_type/value_type/value_type_spec.rb
|
|
240
231
|
- spec/simplecov_helper.rb
|
|
241
232
|
- spec/spec_helper.rb
|
|
233
|
+
- spec/support/reduce_exceptions_helper.rb
|
|
242
234
|
- TODO
|
|
243
235
|
homepage: http://github.com/cjheath/activefacts-api
|
|
244
|
-
licenses:
|
|
236
|
+
licenses:
|
|
245
237
|
- MIT
|
|
246
238
|
post_install_message:
|
|
247
239
|
rdoc_options: []
|
|
248
|
-
|
|
240
|
+
|
|
241
|
+
require_paths:
|
|
249
242
|
- lib
|
|
250
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
|
243
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
251
244
|
none: false
|
|
252
|
-
requirements:
|
|
253
|
-
- -
|
|
254
|
-
- !ruby/object:Gem::Version
|
|
255
|
-
|
|
256
|
-
segments:
|
|
245
|
+
requirements:
|
|
246
|
+
- - ">="
|
|
247
|
+
- !ruby/object:Gem::Version
|
|
248
|
+
hash: 3
|
|
249
|
+
segments:
|
|
257
250
|
- 0
|
|
258
|
-
|
|
259
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
251
|
+
version: "0"
|
|
252
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
260
253
|
none: false
|
|
261
|
-
requirements:
|
|
262
|
-
- -
|
|
263
|
-
- !ruby/object:Gem::Version
|
|
264
|
-
|
|
254
|
+
requirements:
|
|
255
|
+
- - ">="
|
|
256
|
+
- !ruby/object:Gem::Version
|
|
257
|
+
hash: 3
|
|
258
|
+
segments:
|
|
259
|
+
- 0
|
|
260
|
+
version: "0"
|
|
265
261
|
requirements: []
|
|
262
|
+
|
|
266
263
|
rubyforge_project:
|
|
267
264
|
rubygems_version: 1.8.24
|
|
268
265
|
signing_key:
|
|
269
266
|
specification_version: 3
|
|
270
267
|
summary: A fact-based data model DSL and API
|
|
271
268
|
test_files: []
|
|
269
|
+
|