Class: ActiveFacts::API::Constellation
- Defined in:
- lib/activefacts/api/constellation.rb
Overview
A Constellation is a population of instances of the ObjectType classes of a Vocabulary. Every object_type 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
-
#vocabulary ⇒ Object
readonly
Returns the value of attribute vocabulary.
Instance Method Summary collapse
- #assert(klass, *args) ⇒ Object
- #candidate(instance) ⇒ Object
- #define_class_accessor(m, klass) ⇒ Object
-
#deindex_instance(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.
- #has_candidate(klass, key) ⇒ Object
-
#initialize(vocabulary, options = {}) ⇒ Constellation
constructor
Create a new empty Constellation over the given Vocabulary.
-
#inspect ⇒ Object
:nodoc:.
-
#instances ⇒ Object
“instances” is an index (keyed by the Class object) of indexes to instances.
- #invalid_object_type(klass) ⇒ Object
-
#method_missing(m, *args, &b) ⇒ Object
If a missing method is the name of a class in the vocabulary module for this constellation, then we want to access the collection of instances of that class, and perhaps assert new ones.
-
#populate(&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.
- #when_admitted(&b) ⇒ Object
-
#with_candidates(&b) ⇒ Object
Candidates is an array of object instances that do not already exist in the constellation but will be added if an assertion succeeds.
Constructor Details
#initialize(vocabulary, options = {}) ⇒ Constellation
Create a new empty Constellation over the given Vocabulary
112 113 114 115 |
# File 'lib/activefacts/api/constellation.rb', line 112 def initialize(vocabulary, = {}) @vocabulary = vocabulary @options = end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(m, *args, &b) ⇒ Object
If a missing method is the name of a class in the vocabulary module for this constellation, then we want to access the collection of instances of that class, and perhaps assert new ones. With no parameters, return the collection of all instances of that object_type. With parameters, assert an instance of the object_type identified by the values passed as args.
169 170 171 172 173 174 175 176 177 |
# File 'lib/activefacts/api/constellation.rb', line 169 def method_missing(m, *args, &b) klass = @vocabulary.const_get(m) if invalid_object_type klass super else define_class_accessor m, klass send(m, *args, &b) end end |
Instance Attribute Details
#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
#assert(klass, *args) ⇒ Object
117 118 119 120 121 |
# File 'lib/activefacts/api/constellation.rb', line 117 def assert(klass, *args) with_candidates do klass.assert_instance self, args end end |
#candidate(instance) ⇒ Object
103 104 105 |
# File 'lib/activefacts/api/constellation.rb', line 103 def candidate instance @candidates << instance unless @candidates[-1] == instance end |
#define_class_accessor(m, klass) ⇒ Object
152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/activefacts/api/constellation.rb', line 152 def define_class_accessor m, klass (class << self; self; end). send(:define_method, m) do |*args| if args.size == 0 # Return the collection of all instances of this class in the constellation: instances[klass] else # Assert a new ground fact (object_type instance) of the specified class, identified by args: assert(klass, *args) end end end |
#deindex_instance(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.
139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/activefacts/api/constellation.rb', line 139 def deindex_instance(instance) #:nodoc: last_irns = nil last_irvs = instance ([instance.class]+instance.class.supertypes_transitive).each do |klass| if instance.is_a?(Entity) and last_irns != (n = klass.) # Build new identifying_role_values only when the identifying_role_names change: last_irvs = instance.(klass) last_irns = n end instances[klass].delete(last_irvs) end end |
#has_candidate(klass, key) ⇒ Object
107 108 109 |
# File 'lib/activefacts/api/constellation.rb', line 107 def has_candidate klass, key @candidates && @candidates.detect{|c| c.is_a?(klass) && c.(klass) == key } end |
#inspect ⇒ Object
:nodoc:
179 180 181 |
# File 'lib/activefacts/api/constellation.rb', line 179 def inspect #:nodoc: "Constellation:#{object_id}" end |
#instances ⇒ Object
“instances” is an index (keyed by the Class object) of indexes to instances. Each instance is indexed for every supertype it has (including multiply-inherited ones). The method_missing definition supports the syntax: c.MyClass.each{|k, v| … }
53 54 55 56 57 58 59 60 |
# File 'lib/activefacts/api/constellation.rb', line 53 def instances @instances ||= Hash.new do |h,k| if reason = invalid_object_type(k) raise InvalidObjectType.new(@vocabulary, k, reason) end h[k] = InstanceIndex.new(self, k, (@options.include?(:sort) ? @options[:sort] : API::sorted)) end end |
#invalid_object_type(klass) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/activefacts/api/constellation.rb', line 37 def invalid_object_type klass case when !klass.is_a?(Class) 'is not a Class' when klass.modspace != @vocabulary "is defined in #{klass.modspace}, not #{@vocabulary.name}" when !klass.respond_to?(:assert_instance) "is not declared as an object type" else nil end end |
#populate(&block) ⇒ Object
Evaluate assertions against the population of this Constellation
124 125 126 127 |
# File 'lib/activefacts/api/constellation.rb', line 124 def populate &block instance_eval(&block) self end |
#retract(*instances) ⇒ Object
Delete instances from the constellation, nullifying (or cascading) the roles each plays
130 131 132 133 134 135 |
# File 'lib/activefacts/api/constellation.rb', line 130 def retract(*instances) Array(instances).each do |i| i.retract end self end |
#verbalise ⇒ Object
Constellations verbalise all members of all classes in alphabetical order, showing non-identifying role values as well
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/activefacts/api/constellation.rb', line 185 def verbalise "Constellation over #{vocabulary.name}:\n" + vocabulary.object_type.keys.sort.map do |object_type| klass = vocabulary.const_get(object_type) single_roles, multiple_roles = klass.all_role. partition{|n, r| r.unique }. map{ |rs| rs.map{|n, r| n}.sort_by(&:to_s) } single_roles -= klass. if (klass.is_entity_type) instances = send(object_type.to_sym) next nil unless instances.size > 0 "\tEvery #{object_type}:\n" + instances.map do |key, instance| s = "\t\t" + instance.verbalise if (single_roles.size > 0) role_values = single_roles.map do |role_name| #p klass, klass.all_role.keys; exit next nil if klass.all_role(role_name).fact_type.is_a?(TypeInheritanceFactType) value = if instance.respond_to?(role_name) value = instance.send(role_name) else instance.class.all_role(role_name) # This role has not yet been realised end [ role_name.to_s.camelcase, value ] end.compact.select do |role_name, value| value end.map do |role_name, value| "#{role_name} = #{value ? value.verbalise : "nil"}" end s += " where " + role_values*", " if role_values.size > 0 end s end * "\n" end.compact*"\n" end |
#when_admitted(&b) ⇒ Object
95 96 97 98 99 100 101 |
# File 'lib/activefacts/api/constellation.rb', line 95 def when_admitted &b if @candidates.nil? b.call self else @on_admission << b end end |
#with_candidates(&b) ⇒ Object
Candidates is an array of object instances that do not already exist in the constellation but will be added if an assertion succeeds. After the assertion is found to be acceptable, these objects are indexed in the constellation and in the counterparts of their identifying roles, and the candidates array is nullified.
67 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 |
# File 'lib/activefacts/api/constellation.rb', line 67 def with_candidates &b # Multiple assignment reduces (s)teps while debugging outermost, @candidates, @on_admission = @candidates.nil?, (@candidates || []), (@on_admission || []) begin b.call rescue Exception # Do not accept any of these candidates, there was a problem: @candidates = [] if outermost raise ensure if outermost while @candidates # Index the accepted instances in the constellation: candidates = @candidates on_admission = @on_admission @candidates = nil @on_admission = nil candidates.each do |instance| instance.class.index_instance(self, instance) end on_admission.each do |b| b.call end end end end end |