Module: ActiveFacts::API::Instance

Included in:
Entity, Value
Defined in:
lib/activefacts/api/instance.rb

Overview

Every Instance of a ObjectType (A Value type or an Entity type) includes the methods of this module:

Defined Under Namespace

Modules: ClassMethods

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#constellationObject (readonly)

What constellation does this Instance belong to (if any):



12
13
14
# File 'lib/activefacts/api/instance.rb', line 12

def constellation
  @constellation
end

Instance Method Details

#initialize(args = []) ⇒ Object

:nodoc:



14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/activefacts/api/instance.rb', line 14

def initialize(args = []) #:nodoc:
  unless (self.class.is_entity_type)
    begin
      super(*args)
    rescue TypeError => e
      if trace(:debug)
        p e; puts e.backtrace*"\n\t"; debugger; true
      end
    rescue ArgumentError => e
      e.message << " constructing a #{self.class}"
      raise
    end
  end
end

#instance_indexObject



49
50
51
# File 'lib/activefacts/api/instance.rb', line 49

def instance_index
  @constellation.send(self.class.basename.to_sym)
end

#is_a?(klass) ⇒ Boolean

Returns:

  • (Boolean)


29
30
31
# File 'lib/activefacts/api/instance.rb', line 29

def is_a? klass
  super || self.class.supertypes_transitive.include?(klass)
end

List entities which have an identifying role played by this object.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/activefacts/api/instance.rb', line 34

def related_entities(indirectly = true, instances = [])
  # Check all roles of this instance
  self.class.all_role.each do |role_name, role|
    # If the counterpart role is not identifying for its object type, skip it
    next unless c = role.counterpart and c.is_identifying

    identified_instances = Array(self.send(role.getter))
    instances.concat(identified_instances)
    identified_instances.each do |instance|
      instance.related_entities(indirectly, instances) if indirectly
    end
  end
  instances
end

#retractObject

De-assign all functional roles and remove from constellation, if any.



54
55
56
57
58
59
60
61
62
63
64
65
66
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/activefacts/api/instance.rb', line 54

def retract
  return unless constellation = @constellation

  unless constellation.loggers.empty?
    # An object may have multiple identifiers, with potentially overlapping role sets
    # Get one copy of each role to use in asserting the instance
    if self.class.is_entity_type
      identifying_role_values = {}
      ([self.class]+self.class.supertypes_transitive).each do |klass|
        klass.identifying_role_names.zip(identifying_role_values(klass)).each do |name, value|
          identifying_role_values[name] = value
        end
      end
    else
      identifying_role_values = self
    end
  end

  # Delete from the constellation first, while we remember our identifying role values
  constellation.deindex_instance(self)
  instance_variable_set(@@constellation_variable_name ||= "@constellation", nil)

  # Now, for all roles (from this class and all supertypes), assign nil to all functional roles
  # The counterpart roles get cleared automatically.
  klasses = [self.class]+self.class.supertypes_transitive

  irvrvs = {}  # identifying_role_values by RoleValues
  self.class.all_role_transitive.each do |_, role|
    next unless role.counterpart and
      role.unique and
      !role.counterpart.unique and
      counterpart = send(role.getter)
    role_values = counterpart.send(role.counterpart.getter)
    irvrvs[role_values] = role_values.index_values(self)
  end

  # Nullify the counterpart role of objects we identify first, before damaging our identifying_role_values:
  klasses.each do |klass|
    klass.all_role.each do |role_name, role|
      next if role.unary?
      next if !(counterpart = role.counterpart).is_identifying
      next if role.fact_type.is_a?(TypeInheritanceFactType)

      counterpart_instances = send(role.getter)
      counterpart_instances.to_a.each do |counterpart_instance|
        # Allow nullifying non-mandatory roles, as long as they're not identifying.
        if counterpart.mandatory
          counterpart_instance.retract
        else
          counterpart_instance.send(counterpart.setter, nil, false)
        end
      end
    end
  end

  # Now deal with other roles:
  klasses.each do |klass|
    klass.all_role.each do |role_name, role|
      next if role.unary?
      counterpart = role.counterpart

      # Objects being created do not have to have non-identifying mandatory roles,
      # so we allow retracting to the same state.
      if role.unique
        next if role.fact_type.is_a?(TypeInheritanceFactType)
        counterpart_instance = send(role.getter)
        next unless counterpart_instance && counterpart_instance.constellation

        if (counterpart.unique)
          # REVISIT: This will incorrectly fail to propagate a key change for a non-mandatory role
          counterpart_instance.send(counterpart.setter, nil, false)
        else
          rv = counterpart_instance.send(role.counterpart.getter)
          rv.delete_instance(self, irvrvs[rv])

          if (rv.empty? && !counterpart_instance.class.is_entity_type)
            counterpart_instance.retract if counterpart_instance.plays_no_role
          end

        end
        instance_variable_set(role.variable, nil)
      else
        # puts "Not removing role #{role_name} from counterpart RoleValues #{counterpart.name}"
        # Duplicate the array using to_a, as the RoleValues here will be modified as we traverse it:
        next if role.fact_type.is_a?(TypeInheritanceFactType)
        counterpart_instances = send(role.getter)
        counterpart_instances.to_a.each do |counterpart_instance|
          next unless counterpart_instance.constellation
          # This action deconstructs our RoleValues as we go:
          if counterpart.mandatory
            counterpart_instance.retract
          else
            counterpart_instance.send(counterpart.setter, nil, false)
          end
        end
        instance_variable_set(role.variable, nil)
      end
    end
  end

  constellation.loggers.each{|l| l.call(:retract, self.class, identifying_role_values) }

end