Class: ActiveFacts::API::Constellation

Inherits:
Object
  • Object
show all
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 ##delete 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

Instance Method Summary collapse

Constructor Details

#initialize(vocabulary) ⇒ Constellation

Create a new empty Constellation over the given Vocabulary



40
41
42
43
# File 'lib/activefacts/api/constellation.rb', line 40

def initialize(vocabulary)
  @vocabulary = vocabulary
  @instances = Hash.new{|h,k| h[k] = InstanceIndex.new }
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.



59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/activefacts/api/constellation.rb', line 59

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

#instancesObject (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!



36
37
38
# File 'lib/activefacts/api/constellation.rb', line 36

def instances
  @instances
end

#vocabularyObject (readonly)

Returns the value of attribute vocabulary.



34
35
36
# File 'lib/activefacts/api/constellation.rb', line 34

def vocabulary
  @vocabulary
end

Instance Method Details

#delete(instance) ⇒ Object

This method removes the given instance from this constellation’s indexes



50
51
52
53
54
55
# File 'lib/activefacts/api/constellation.rb', line 50

def delete(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
end

#inspectObject

:nodoc:



45
46
47
# File 'lib/activefacts/api/constellation.rb', line 45

def inspect #:nodoc:
  "Constellation:#{object_id}"
end

#verbaliseObject

Constellations verbalise all members of all classes in alphabetical order, showing non-identifying role values as well



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/activefacts/api/constellation.rb', line 75

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.identifying_role_names if (klass.respond_to?(:identifying_role_names))
      # 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