Class: ActiveFacts::API::Constellation
- Inherits:
-
Object
- Object
- ActiveFacts::API::Constellation
- Defined in:
- lib/activefacts/api/constellation.rb
Overview
A Constellation is a population of instances of the Concept classes of a Vocabulary. Every concept class is either a Value type or an Entity type.
Value types are uniquely identified by their value, and a constellation will only ever have a single instance of a given value of that class.
Entity instances are uniquely identified by their identifying roles, and again, a constellation will only ever have a single entity instance for the values of those identifying roles.
As a result, you cannot “create” an object in a constellation - you merely assert its existence. This is done using method_missing; @constellation.Thing(3) creates an instance (or returns the existing instance) of Thing identified by the value 3. You can also use the populate() method to apply a block of assertions.
You can instance##retract any instance, and that removes it from the constellation (will delete it from the database when the constellation is saved), and nullifies any references to it.
A Constellation may or not be valid according to the vocabulary’s constraints, but it may also represent a portion of a larger population (a database) with which it may be merged to form a valid population. In other words, an invalid Constellation may be invalid only because it lacks some of the facts.
Instance Attribute Summary collapse
-
#instances ⇒ Object
readonly
All instances are indexed in this hash, keyed by the class object.
-
#vocabulary ⇒ Object
readonly
Returns the value of attribute vocabulary.
Instance Method Summary collapse
-
#__retract(instance) ⇒ Object
This method removes the given instance from this constellation’s indexes It must be called before the identifying roles get deleted or nullified.
-
#initialize(vocabulary) ⇒ Constellation
constructor
Create a new empty Constellation over the given Vocabulary.
-
#inspect ⇒ Object
:nodoc:.
-
#method_missing(m, *args) ⇒ Object
With parameters, assert an instance of the concept whose name is the missing method, identified by the values passed as args.
-
#populate(*args, &block) ⇒ Object
Evaluate assertions against the population of this Constellation.
-
#retract(*instances) ⇒ Object
Delete instances from the constellation, nullifying (or cascading) the roles each plays.
-
#verbalise ⇒ Object
Constellations verbalise all members of all classes in alphabetical order, showing non-identifying role values as well.
Constructor Details
#initialize(vocabulary) ⇒ Constellation
Create a new empty Constellation over the given Vocabulary
41 42 43 44 45 46 47 |
# File 'lib/activefacts/api/constellation.rb', line 41 def initialize(vocabulary) @vocabulary = vocabulary @instances = Hash.new do |h,k| raise "A constellation over #{@vocabulary.name} can only index instances of concepts in that vocabulary, not #{k.inspect}" unless k.is_a?(Class) and k.modspace == vocabulary h[k] = InstanceIndex.new end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(m, *args) ⇒ Object
With parameters, assert an instance of the concept whose name is the missing method, identified by the values passed as args. With no parameters, return the collection of all instances of that concept.
113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/activefacts/api/constellation.rb', line 113 def method_missing(m, *args) if klass = @vocabulary.const_get(m) if args.size == 0 # Return the collection of all instances of this class in the constellation: @instances[klass] else # Assert a new ground fact (concept instance) of the specified class, identified by args: # REVISIT: create a constructor method here instead? instance, key = klass.assert_instance(self, args) instance end end end |
Instance Attribute Details
#instances ⇒ Object (readonly)
All instances are indexed in this hash, keyed by the class object. Each instance is indexed for every supertype it has (including multiply-inherited ones). It’s a bad idea to try to modify these indexes!
37 38 39 |
# File 'lib/activefacts/api/constellation.rb', line 37 def instances @instances end |
#vocabulary ⇒ Object (readonly)
Returns the value of attribute vocabulary.
35 36 37 |
# File 'lib/activefacts/api/constellation.rb', line 35 def vocabulary @vocabulary end |
Instance Method Details
#__retract(instance) ⇒ Object
This method removes the given instance from this constellation’s indexes It must be called before the identifying roles get deleted or nullified.
102 103 104 105 106 107 108 109 |
# File 'lib/activefacts/api/constellation.rb', line 102 def __retract(instance) #:nodoc: # REVISIT: Need to search, as key values are gone already. Is there a faster way? ([instance.class]+instance.class.supertypes_transitive).each do |klass| @instances[klass].delete_if{|k,v| v == instance } end # REVISIT: Need to nullify all the roles this object plays. # If mandatory on the counterpart side, this may/must propagate the delete (without mutual recursion!) end |
#inspect ⇒ Object
:nodoc:
49 50 51 |
# File 'lib/activefacts/api/constellation.rb', line 49 def inspect #:nodoc: "Constellation:#{object_id}" end |
#populate(*args, &block) ⇒ Object
Evaluate assertions against the population of this Constellation
54 55 56 57 |
# File 'lib/activefacts/api/constellation.rb', line 54 def populate *args, &block # REVISIT: Use args for something? Like options to enable/disable validation? instance_eval(&block) end |
#retract(*instances) ⇒ Object
Delete instances from the constellation, nullifying (or cascading) the roles each plays
60 61 62 63 64 |
# File 'lib/activefacts/api/constellation.rb', line 60 def retract(*instances) Array(instances).each do |i| i.retract end end |
#verbalise ⇒ Object
Constellations verbalise all members of all classes in alphabetical order, showing non-identifying role values as well
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/activefacts/api/constellation.rb', line 68 def verbalise "Constellation over #{vocabulary.name}:\n" + vocabulary.concept.keys.sort.map{|concept| klass = vocabulary.const_get(concept) # REVISIT: It would be better not to rely on the role name pattern here: single_roles, multiple_roles = klass.roles.keys.sort_by(&:to_s).partition{|r| r.to_s !~ /\Aall_/ } single_roles -= klass. if (klass.is_entity_type) # REVISIT: Need to include superclass roles also. instances = send(concept.to_sym) next nil unless instances.size > 0 "\tEvery #{concept}:\n" + instances.map{|key, instance| s = "\t\t" + instance.verbalise if (single_roles.size > 0) role_values = single_roles.map{|role| [ role_name = role.to_s.camelcase(true), value = instance.send(role)] }.select{|role_name, value| value }.map{|role_name, value| "#{role_name} = #{value ? value.verbalise : "nil"}" } s += " where " + role_values*", " if role_values.size > 0 end s } * "\n" }.compact*"\n" end |