activefacts-api 1.9.5 → 1.9.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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