activefacts-api 1.9.5 → 1.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.
@@ -17,13 +17,13 @@ class ::Date
17
17
  elsif (a.size == 1)
18
18
  case a[0]
19
19
  when DateTime
20
- d = civil(a[0].year, a[0].month, a[0].day, a[0].start)
20
+ d = civil(a[0].year, a[0].month, a[0].day, a[0].start)
21
21
  when Date
22
- d = civil(a[0].year, a[0].month, a[0].day, a[0].start)
22
+ d = civil(a[0].year, a[0].month, a[0].day, a[0].start)
23
23
  when NilClass
24
- d = civil()
24
+ d = civil()
25
25
  else
26
- d = civil(*a, &b)
26
+ d = civil(*a, &b)
27
27
  end
28
28
  else
29
29
  d = civil(*a, &b)
@@ -42,13 +42,13 @@ class ::DateTime
42
42
  elsif (a.size == 1)
43
43
  case a[0]
44
44
  when DateTime
45
- dt = civil(a[0].year, a[0].month, a[0].day, a[0].hour, a[0].min, a[0].sec, a[0].start)
45
+ dt = civil(a[0].year, a[0].month, a[0].day, a[0].hour, a[0].min, a[0].sec, a[0].start)
46
46
  when Date
47
- dt = civil(a[0].year, a[0].month, a[0].day, 0, 0, 0, a[0].start)
47
+ dt = civil(a[0].year, a[0].month, a[0].day, 0, 0, 0, a[0].start)
48
48
  when NilClass
49
- dt = civil()
49
+ dt = civil()
50
50
  else
51
- dt = civil(*a, &b)
51
+ dt = civil(*a, &b)
52
52
  end
53
53
  else
54
54
  dt = civil(*a, &b)
@@ -26,38 +26,38 @@ 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 ArgumentError.new("#{self}.new expects a hash. You should use assert instead anyhow") unless arg_hash.is_a?(Hash)
30
- super(arg_hash) # Initialise the Instance
31
- initialize_existential_roles(self.class, arg_hash)
29
+ raise ArgumentError.new("#{self}.new expects a hash. You should use assert instead anyhow") unless arg_hash.is_a?(Hash)
30
+ super(arg_hash) # Initialise the Instance
31
+ initialize_existential_roles(self.class, arg_hash)
32
32
  end
33
33
 
34
34
  def initialize_existential_roles(klass, arg_hash)
35
- # If overrides_identification_of, assign those attributes too (recursively)
36
- if o = klass.overrides_identification_of
37
- initialize_existential_roles(o, arg_hash)
38
- end
39
-
40
- irns = klass.identifying_role_names
41
- irns.each do |role_name|
42
- role = klass.all_role(role_name)
43
- key = arg_hash.delete(role_name)
44
- value =
45
- if key == nil
46
- nil
47
- elsif role.unary?
48
- (key && true) # Preserve nil and false
49
- else
50
- role.counterpart.object_type.assert_instance(constellation, Array(key))
51
- end
52
-
53
- begin
54
- unless instance_variable_get(role.variable) != nil # Not if it was set by a superclass identifier
55
- send(role.setter, value, ObjectType::CHECKED_IDENTIFYING_ROLE)
56
- end
57
- rescue NoMethodError => e
58
- raise settable_roles_exception(e, role_name)
59
- end
60
- end
35
+ # If overrides_identification_of, assign those attributes too (recursively)
36
+ if o = klass.overrides_identification_of
37
+ initialize_existential_roles(o, arg_hash)
38
+ end
39
+
40
+ irns = klass.identifying_role_names
41
+ irns.each do |role_name|
42
+ role = klass.all_role(role_name)
43
+ key = arg_hash.delete(role_name)
44
+ value =
45
+ if key == nil
46
+ nil
47
+ elsif role.unary?
48
+ (key && true) # Preserve nil and false
49
+ else
50
+ role.counterpart.object_type.assert_instance(constellation, Array(key))
51
+ end
52
+
53
+ begin
54
+ unless instance_variable_get(role.variable) != nil # Not if it was set by a superclass identifier
55
+ send(role.setter, value, ObjectType::CHECKED_IDENTIFYING_ROLE)
56
+ end
57
+ rescue NoMethodError => e
58
+ raise settable_roles_exception(e, role_name)
59
+ end
60
+ end
61
61
  end
62
62
 
63
63
  # This exception is raised when an entity is instantiated before the
@@ -133,9 +133,9 @@ module ActiveFacts
133
133
  def identifying_role_values(klass = self.class)
134
134
  klass.identifying_roles.map do |role|
135
135
  value = send(role.name)
136
- counterpart_class = role.counterpart && role.counterpart.object_type
137
- value.identifying_role_values(counterpart_class)
138
- end
136
+ counterpart_class = role.counterpart && role.counterpart.object_type
137
+ value.identifying_role_values(counterpart_class)
138
+ end
139
139
  end
140
140
 
141
141
  # Identifying role values in a hash form.
@@ -161,48 +161,48 @@ module ActiveFacts
161
161
  # class for each such instance.
162
162
  # This function is transitive!
163
163
  def analyse_impacts role
164
- impacts = []
165
-
166
- # Consider the object itself and all its supertypes
167
- ([self.class]+self.class.supertypes_transitive).map do |supertype|
168
- next unless supertype.identifying_roles.include?(role)
169
-
170
- old_key = identifying_role_values(supertype)
171
- # puts "Need to reindex #{self.class} as #{supertype} from #{old_key.inspect}"
172
- impacts << [supertype, self, old_key]
173
- end
174
-
175
- # Now consider objects whose identifiers include this object.
176
- # Find our roles in those identifiers first.
177
- impacted_roles = []
178
- self.class.all_role_transitive.each do |n, role|
179
- if role.counterpart && role.counterpart.is_identifying
180
- # puts "Changing #{role.inspect} affects #{role.inspect}"
181
- impacted_roles << role
182
- end
183
- end
184
-
185
- impacted_roles.each do |role|
186
- affected_instances = Array(instance_variable_get(role.variable))
187
- # puts "considering #{affected_instances.size} #{role.object_type.name} instances that include #{role.inspect}: #{affected_instances.map(&:identifying_role_values).inspect}"
188
- affected_instances.each do |counterpart|
189
- impacts.concat(counterpart.analyse_impacts(role.counterpart))
190
- end
191
- end
192
- impacts
164
+ impacts = []
165
+
166
+ # Consider the object itself and all its supertypes
167
+ ([self.class]+self.class.supertypes_transitive).map do |supertype|
168
+ next unless supertype.identifying_roles.include?(role)
169
+
170
+ old_key = identifying_role_values(supertype)
171
+ # puts "Need to reindex #{self.class} as #{supertype} from #{old_key.inspect}"
172
+ impacts << [supertype, self, old_key]
173
+ end
174
+
175
+ # Now consider objects whose identifiers include this object.
176
+ # Find our roles in those identifiers first.
177
+ impacted_roles = []
178
+ self.class.all_role_transitive.each do |n, role|
179
+ if role.counterpart && role.counterpart.is_identifying
180
+ # puts "Changing #{role.inspect} affects #{role.inspect}"
181
+ impacted_roles << role
182
+ end
183
+ end
184
+
185
+ impacted_roles.each do |role|
186
+ affected_instances = Array(instance_variable_get(role.variable))
187
+ # puts "considering #{affected_instances.size} #{role.object_type.name} instances that include #{role.inspect}: #{affected_instances.map(&:identifying_role_values).inspect}"
188
+ affected_instances.each do |counterpart|
189
+ impacts.concat(counterpart.analyse_impacts(role.counterpart))
190
+ end
191
+ end
192
+ impacts
193
193
  end
194
194
 
195
195
  def apply_impacts impacts
196
- impacts.each do |klass, entity, old_key|
197
- instance_index = entity.constellation.instances[klass]
198
- new_key = entity.identifying_role_values(klass)
199
- # puts "Reindexing #{klass} from #{old_key.inspect} to #{new_key.inspect}"
200
-
201
- if new_key != old_key
202
- instance_index.delete(old_key)
203
- instance_index[new_key] = entity
204
- end
205
- end
196
+ impacts.each do |klass, entity, old_key|
197
+ instance_index = entity.constellation.instances[klass]
198
+ new_key = entity.identifying_role_values(klass)
199
+ # puts "Reindexing #{klass} from #{old_key.inspect} to #{new_key.inspect}"
200
+
201
+ if new_key != old_key
202
+ instance_index.delete(old_key)
203
+ instance_index[new_key] = entity
204
+ end
205
+ end
206
206
  end
207
207
 
208
208
  # If this instance's role is updated to the new value, does that cause a collision?
@@ -210,22 +210,22 @@ module ActiveFacts
210
210
  def check_identification_change_legality(role, value)
211
211
  return unless @constellation && role.is_identifying
212
212
 
213
- klasses = [self.class] + self.class.supertypes_transitive
214
- last_identity = nil
215
- last_irns = nil
216
- counterpart_class = role.counterpart ? role.counterpart.object_type : value.class
213
+ klasses = [self.class] + self.class.supertypes_transitive
214
+ last_identity = nil
215
+ last_irns = nil
216
+ counterpart_class = role.counterpart ? role.counterpart.object_type : value.class
217
217
  duplicate = klasses.detect do |klass|
218
218
  next false unless klass.identifying_roles.include?(role)
219
- irns = klass.identifying_role_names
220
- if last_irns != irns
221
- last_identity = identifying_role_values(klass)
222
- role_position = irns.index(role.name)
223
- last_identity[role_position] = value.identifying_role_values(counterpart_class)
224
- end
225
- @constellation.instances[klass][last_identity]
219
+ irns = klass.identifying_role_names
220
+ if last_irns != irns
221
+ last_identity = identifying_role_values(klass)
222
+ role_position = irns.index(role.name)
223
+ last_identity[role_position] = value.identifying_role_values(counterpart_class)
224
+ end
225
+ @constellation.instances[klass][last_identity]
226
226
  end
227
227
 
228
- raise DuplicateIdentifyingValueException.new(self.class, role.name, value) if duplicate
228
+ raise DuplicateIdentifyingValueException.new(self.class, role.name, value) if duplicate
229
229
  end
230
230
 
231
231
  # All classes that become Entity types receive the methods of this class as class methods:
@@ -249,7 +249,7 @@ module ActiveFacts
249
249
  @identifying_roles ||=
250
250
  identifying_role_names.map do |role_name|
251
251
  role = all_role[role_name] || find_inherited_role(role_name)
252
- raise "Illegal request for identifying_roles of #{self} before they're all defined" if role == false
252
+ raise "Illegal request for identifying_roles of #{self} before they're all defined" if role == false
253
253
  role
254
254
  end.freeze
255
255
  end
@@ -264,154 +264,154 @@ module ActiveFacts
264
264
  end
265
265
  end
266
266
 
267
- def check_supertype_identifiers_match instance, arg_hash
268
- supertypes_transitive.each do |supertype|
269
- supertype.identifying_roles.each do |role|
270
- next unless arg_hash.include?(role.name) # No contradiction here
271
- new_value = arg_hash[role.name]
272
- existing_value = instance.send(role.name.to_sym)
273
-
274
- # Quick check for an exact match:
275
- counterpart_class = role.counterpart && role.counterpart.object_type
276
- next if existing_value == new_value or existing_value.identifying_role_values(counterpart_class) == new_value
277
-
278
- # Coerce the new value to identifying values for the counterpart role's type:
279
- role = supertype.all_role(role.name)
280
- new_key = role.counterpart.object_type.identifying_role_values(instance.constellation, [new_value])
281
- next if existing_value == new_key # This can happen when the counterpart is a value type
282
-
283
- existing_key = existing_value.identifying_role_values(counterpart_class)
284
- next if existing_key == new_key
285
- raise TypeConflictException.new(basename, supertype, new_key, existing_key)
286
- end
287
- end
288
- end
289
-
290
- # all its candidate keys must match those from the arg_hash.
291
- def check_no_supertype_instance_exists constellation, arg_hash
292
- supertypes_transitive.each do |supertype|
293
- key = supertype.identifying_role_values(constellation, [arg_hash])
294
- if constellation.instances[supertype][key]
295
- raise TypeMigrationException.new(basename, supertype, key)
296
- end
297
- end
298
- end
299
-
300
- # This method receives an array (possibly including a trailing arguments hash)
301
- # from which the values of identifying roles must be coerced. Note that when a
302
- # value which is not the corrent class is received, we recurse to ask that class
303
- # to coerce what we *do* have.
304
- # The return value is an array of (and arrays of) raw values, not object instances.
305
- #
306
- # No new instances may be asserted, nor may any roles of objects in the constellation be changed
307
- def identifying_role_values(constellation, args)
267
+ def check_supertype_identifiers_match instance, arg_hash
268
+ supertypes_transitive.each do |supertype|
269
+ supertype.identifying_roles.each do |role|
270
+ next unless arg_hash.include?(role.name) # No contradiction here
271
+ new_value = arg_hash[role.name]
272
+ existing_value = instance.send(role.name.to_sym)
273
+
274
+ # Quick check for an exact match:
275
+ counterpart_class = role.counterpart && role.counterpart.object_type
276
+ next if existing_value == new_value or existing_value.identifying_role_values(counterpart_class) == new_value
277
+
278
+ # Coerce the new value to identifying values for the counterpart role's type:
279
+ role = supertype.all_role(role.name)
280
+ new_key = role.counterpart.object_type.identifying_role_values(instance.constellation, [new_value])
281
+ next if existing_value == new_key # This can happen when the counterpart is a value type
282
+
283
+ existing_key = existing_value.identifying_role_values(counterpart_class)
284
+ next if existing_key == new_key
285
+ raise TypeConflictException.new(basename, supertype, new_key, existing_key)
286
+ end
287
+ end
288
+ end
289
+
290
+ # all its candidate keys must match those from the arg_hash.
291
+ def check_no_supertype_instance_exists constellation, arg_hash
292
+ supertypes_transitive.each do |supertype|
293
+ key = supertype.identifying_role_values(constellation, [arg_hash])
294
+ if constellation.instances[supertype][key]
295
+ raise TypeMigrationException.new(basename, supertype, key)
296
+ end
297
+ end
298
+ end
299
+
300
+ # This method receives an array (possibly including a trailing arguments hash)
301
+ # from which the values of identifying roles must be coerced. Note that when a
302
+ # value which is not the corrent class is received, we recurse to ask that class
303
+ # to coerce what we *do* have.
304
+ # The return value is an array of (and arrays of) raw values, not object instances.
305
+ #
306
+ # No new instances may be asserted, nor may any roles of objects in the constellation be changed
307
+ def identifying_role_values(constellation, args)
308
308
  irns = identifying_role_names
309
309
 
310
- # Normalise positional arguments into an arguments hash (this changes the passed parameter)
311
- arg_hash = args[-1].is_a?(Hash) ? args.pop : {}
310
+ # Normalise positional arguments into an arguments hash (this changes the passed parameter)
311
+ arg_hash = args[-1].is_a?(Hash) ? args.pop : {}
312
312
 
313
- # If the first parameter is an object of type self, its
314
- # identifying roles provide any values missing from the array/hash.
315
- if args[0].is_a?(self)
316
- proto = args.shift
317
- end
313
+ # If the first parameter is an object of type self, its
314
+ # identifying roles provide any values missing from the array/hash.
315
+ if args[0].is_a?(self)
316
+ proto = args.shift
317
+ end
318
318
 
319
- # Following arguments provide identifying values in sequence; put them into the hash:
320
- irns.each do |role_name|
321
- break if args.size == 0
322
- arg_hash[role_name] = args.shift
323
- end
319
+ # Following arguments provide identifying values in sequence; put them into the hash:
320
+ irns.each do |role_name|
321
+ break if args.size == 0
322
+ arg_hash[role_name] = args.shift
323
+ end
324
324
 
325
- # Complain if we have left-over arguments
326
- if args.size > 0
325
+ # Complain if we have left-over arguments
326
+ if args.size > 0
327
327
  raise UnexpectedIdentifyingValueException.new(self, irns, args)
328
- end
329
-
330
- # The arg_hash will be used to construct a new instance, if necessary
331
- args.push(arg_hash)
332
-
333
- irns.map do |role_name|
334
- all_role(role_name)
335
- end.map do |role|
336
- if arg_hash.include?(n = role.name) # Do it this way to avoid problems where nil or false is provided
337
- value = arg_hash[n]
338
- next (value && true) if (role.unary?)
339
- if value
340
- klass = role.counterpart.object_type
341
- value = klass.identifying_role_values(constellation, Array(value))
342
- end
343
- elsif proto
344
- value = proto.send(n)
345
- counterpart_class = role.counterpart && role.counterpart.object_type
346
- value = value.identifying_role_values(counterpart_class)
347
- arg_hash[n] = value # Save the value for making a new instance
348
- next value if (role.unary?)
349
- else
350
- value = nil
351
- end
352
-
353
- raise MissingMandatoryRoleValueException.new(self, role) if value.nil? && role.mandatory
354
-
355
- value
356
- end
357
- end
358
-
359
- def assert_instance(constellation, args)
360
- key = identifying_role_values(constellation, args)
361
-
362
- # The args is now normalized to an array containing a single Hash element
363
- arg_hash = args[-1]
364
-
365
- # Find or make an instance of the class:
328
+ end
329
+
330
+ # The arg_hash will be used to construct a new instance, if necessary
331
+ args.push(arg_hash)
332
+
333
+ irns.map do |role_name|
334
+ all_role(role_name)
335
+ end.map do |role|
336
+ if arg_hash.include?(n = role.name) # Do it this way to avoid problems where nil or false is provided
337
+ value = arg_hash[n]
338
+ next (value && true) if (role.unary?)
339
+ if value
340
+ klass = role.counterpart.object_type
341
+ value = klass.identifying_role_values(constellation, Array(value))
342
+ end
343
+ elsif proto
344
+ value = proto.send(n)
345
+ counterpart_class = role.counterpart && role.counterpart.object_type
346
+ value = value.identifying_role_values(counterpart_class)
347
+ arg_hash[n] = value # Save the value for making a new instance
348
+ next value if (role.unary?)
349
+ else
350
+ value = nil
351
+ end
352
+
353
+ raise MissingMandatoryRoleValueException.new(self, role) if value.nil? && role.mandatory
354
+
355
+ value
356
+ end
357
+ end
358
+
359
+ def assert_instance(constellation, args)
360
+ key = identifying_role_values(constellation, args)
361
+
362
+ # The args is now normalized to an array containing a single Hash element
363
+ arg_hash = args[-1]
364
+
365
+ # Find or make an instance of the class:
366
366
  instance_index = constellation.instances[self] # All instances of this class in this constellation
367
- instance = constellation.has_candidate(self, key) || instance_index[key]
368
- if (instance)
369
- # Check that all assertions about supertype keys are non-contradictory
370
- check_supertype_identifiers_match(instance, arg_hash)
371
- else
372
- # Check that no instance of any supertype matches the keys given
373
- check_no_supertype_instance_exists(constellation, arg_hash)
374
-
375
- instance = new_instance(constellation, arg_hash)
376
- constellation.candidate(instance)
377
- end
378
-
379
- # Assign any extra roles that may have been passed.
380
- # An exception here leaves the object indexed,
381
- # but without the offending role (re-)assigned.
382
- arg_hash.each do |k, v|
383
- role = instance.class.all_role(k)
384
- unless role.is_identifying && role.object_type == self
385
- value =
386
- if v == nil
387
- nil
388
- elsif role.unary?
389
- (v && true) # Preserve nil and false
390
- else
391
- role.counterpart.object_type.assert_instance(constellation, Array(v))
392
- end
393
- constellation.when_admitted {
394
- instance.send(:"#{k}=", value)
395
- }
396
- end
397
- end
398
-
399
- instance
400
- end
367
+ instance = constellation.has_candidate(self, key) || instance_index[key]
368
+ if (instance)
369
+ # Check that all assertions about supertype keys are non-contradictory
370
+ check_supertype_identifiers_match(instance, arg_hash)
371
+ else
372
+ # Check that no instance of any supertype matches the keys given
373
+ check_no_supertype_instance_exists(constellation, arg_hash)
374
+
375
+ instance = new_instance(constellation, arg_hash)
376
+ constellation.candidate(instance)
377
+ end
378
+
379
+ # Assign any extra roles that may have been passed.
380
+ # An exception here leaves the object indexed,
381
+ # but without the offending role (re-)assigned.
382
+ arg_hash.each do |k, v|
383
+ role = instance.class.all_role(k)
384
+ unless role.is_identifying && role.object_type == self
385
+ value =
386
+ if v == nil
387
+ nil
388
+ elsif role.unary?
389
+ (v && true) # Preserve nil and false
390
+ else
391
+ role.counterpart.object_type.assert_instance(constellation, Array(v))
392
+ end
393
+ constellation.when_admitted {
394
+ instance.send(:"#{k}=", value)
395
+ }
396
+ end
397
+ end
398
+
399
+ instance
400
+ end
401
401
 
402
402
  def index_instance(constellation, instance) #:nodoc:
403
- # Index the instance in the constellation's InstanceIndex for this class:
404
- instance_index = constellation.instances[self]
405
- key = instance.identifying_role_values(self)
406
- instance_index[key] = instance
403
+ # Index the instance in the constellation's InstanceIndex for this class:
404
+ instance_index = constellation.instances[self]
405
+ key = instance.identifying_role_values(self)
406
+ instance_index[key] = instance
407
407
 
408
408
  # Index the instance for each supertype:
409
- supertypes.each do |supertype|
410
- supertype.index_instance(constellation, instance)
411
- end
409
+ supertypes.each do |supertype|
410
+ supertype.index_instance(constellation, instance)
411
+ end
412
412
 
413
- instance
414
- end
413
+ instance
414
+ end
415
415
 
416
416
  # A object_type that isn't a ValueType must have an identification scheme,
417
417
  # which is a list of roles it plays. The identification scheme may be
@@ -419,7 +419,7 @@ module ActiveFacts
419
419
  def identified_by(*args) #:nodoc:
420
420
  options = (args[-1].is_a?(Hash) ? args.pop : {})
421
421
  options.each do |key, value|
422
- raise UnrecognisedOptionsException.new('EntityType', basename, key) unless respond_to?(key)
422
+ raise UnrecognisedOptionsException.new('EntityType', basename, key) unless respond_to?(key)
423
423
  send(key, value)
424
424
  end
425
425
 
@@ -444,7 +444,7 @@ module ActiveFacts
444
444
  def inherited(other) #:nodoc:
445
445
  other.identification_inherited_from = self
446
446
  subtypes << other unless subtypes.include? other
447
- TypeInheritanceFactType.new(self, other)
447
+ TypeInheritanceFactType.new(self, other)
448
448
  vocabulary.__add_object_type(other)
449
449
  end
450
450
 
@@ -457,12 +457,12 @@ module ActiveFacts
457
457
  def self.included other #:nodoc:
458
458
  other.send :extend, ClassMethods
459
459
 
460
- def other.new_instance constellation, *args
461
- instance = allocate
462
- instance.instance_variable_set(@@constellation_variable_name ||= "@constellation", constellation)
463
- instance.send(:initialize, *args)
464
- instance
465
- end
460
+ def other.new_instance constellation, *args
461
+ instance = allocate
462
+ instance.instance_variable_set(@@constellation_variable_name ||= "@constellation", constellation)
463
+ instance.send(:initialize, *args)
464
+ instance
465
+ end
466
466
 
467
467
  # Register ourselves with the parent module, which has become a Vocabulary:
468
468
  vocabulary = other.modspace