Module: ActiveFacts::API::Entity::ClassMethods

Includes:
Instance::ClassMethods
Defined in:
lib/activefacts/api/entity.rb

Overview

All classes that become Entity types receive the methods of this class as class methods:

Instance Method Summary collapse

Methods included from Concept

#__absorb, #columns, #has_one, #is_a?, #is_table, #maybe, #one_to_one, #realise_role, #roles, #subtypes, #supertypes, #supertypes_transitive, #table, #vocabulary

Instance Method Details

#assert_instance(constellation, args) ⇒ Object

:nodoc:



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/activefacts/api/entity.rb', line 146

def assert_instance(constellation, args) #:nodoc:
  # Build the key for this instance from the args
  # The key of an instance is the value or array of keys of the identifying values.
  # The key values aren't necessarily present in the constellation, even after this.
  key = identifying_role_values(*args)

  # Find and return an existing instance matching this key
  instances = constellation.instances[self]   # All instances of this class in this constellation
  instance = instances[key]
  # DEBUG: puts "assert #{self.basename} #{key.inspect} #{instance ? "exists" : "new"}"
  return instance, key if instance      # A matching instance of this class

  # Now construct each of this object's identifying roles
  ir = identifying_role_names
  args, arg_hash = ActiveFacts::extract_hash_args(ir, args)
  role_values = ir.map{|role_sym| roles(role_sym)}.zip(args)
  key = []    # Gather the actual key (AutoCounters are special)
  values = role_values.map do |role, arg|
      if !arg
        value = role_key = nil          # No value
      elsif !role.counterpart
        value = role_key = !!arg        # Unary
      elsif arg.is_a?(role.counterpart_concept)      # REVISIT: or a secondary supertype
        arg = arg.__getobj__ if RoleProxy === arg
        raise "Connecting values across constellations" unless arg.constellation == constellation
        value, role_key = arg, arg.identifying_role_values
      else
        value, role_key = role.counterpart_concept.assert_instance(constellation, Array(arg))
      end
      key << role_key
      value
    end
  values << arg_hash if arg_hash and !arg_hash.empty?

  #puts "Creating new #{basename} using #{values.inspect}"
  instance = new(*values)

  # Make the new entity instance a member of this constellation:
  instance.constellation = constellation
  return *index_instance(instance, key, ir)
end

#identifying_role_namesObject

Return the array of Role objects that define the identifying relationships of this Entity type:



108
109
110
# File 'lib/activefacts/api/entity.rb', line 108

def identifying_role_names
  @identifying_role_names ||= []
end

#identifying_role_values(*args) ⇒ Object

Convert the passed arguments into an array of Instance objects that can identify an instance of this Entity type:



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
# File 'lib/activefacts/api/entity.rb', line 113

def identifying_role_values(*args)
  #puts "Getting identifying role values #{identifying_role_names.inspect} of #{basename} using #{args.inspect}"

  # If the single arg is an instance of the correct class or a subclass,
  # use the instance's identifying_role_values
  if (args.size == 1 and
      (arg = args[0]).is_a?(self))       # REVISIT: or a secondary supertype
    arg = arg.__getobj__ if RoleProxy === arg
    return arg.identifying_role_values
  end

  ir = identifying_role_names
  args, arg_hash = ActiveFacts::extract_hash_args(ir, args)
  if args.size < ir.size
    raise "#{basename} requires all identifying values, you're missing #{ir[args.size..-1].map(&:to_sym)*', '}"
  elsif args.size > ir.size
    raise "#{basename} requires all identifying values, you have #{args.size-ir.size} extras #{args[ir.size..-1].map(&:inspect)*', '}"
  end

  role_args = ir.map{|role_sym| roles(role_sym)}.zip(args)
  role_args.map do |role, arg|
    #puts "Getting identifying_role_value for #{role.counterpart_concept.basename} using #{arg.inspect}"
    next nil unless arg
    next !!arg unless role.counterpart  # Unary
    arg = arg.__getobj__ if RoleProxy === arg
    if arg.is_a?(role.counterpart_concept)              # REVISIT: or a secondary supertype
      # Note that with a secondary supertype, it must still return the values of these identifying_role_names
      next arg.identifying_role_values
    end
    role.counterpart_concept.identifying_role_values(*arg)
  end
end

#index_instance(instance, key = nil, key_roles = nil) ⇒ Object

:nodoc:



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/activefacts/api/entity.rb', line 188

def index_instance(instance, key = nil, key_roles = nil) #:nodoc:
  # Derive a new key if we didn't receive one or if the roles are different:
  unless key && key_roles && key_roles == identifying_role_names
    key = (key_roles = identifying_role_names).map do |role_name|
      instance.send role_name
    end
  end

  # Index the instance for this class in the constellation
  instances = instance.constellation.instances[self]
  instances[key] = instance
  # DEBUG: puts "indexing entity #{basename} using #{key.inspect} in #{constellation.object_id}"

  # Index the instance for each supertype:
  supertypes.each do |supertype|
    supertype.index_instance(instance, key, key_roles)
  end

  return instance, key
end

#inherited(other) ⇒ Object

:nodoc:



220
221
222
223
224
225
# File 'lib/activefacts/api/entity.rb', line 220

def inherited(other) #:nodoc:
  other.identified_by *identifying_role_names
  subtypes << other unless subtypes.include? other
  #puts "#{self.name} inherited by #{other.name}"
  vocabulary.add_concept(other)
end

#initialise_entity_type(*args) ⇒ Object

A concept that isn’t a ValueType must have an identification scheme, which is a list of roles it plays. The identification scheme may be inherited from a superclass.



212
213
214
215
216
217
218
# File 'lib/activefacts/api/entity.rb', line 212

def initialise_entity_type(*args) #:nodoc:
  #puts "Initialising entity type #{self} using #{args.inspect}"
  @identifying_role_names = superclass.identifying_role_names if superclass.respond_to?(:identifying_role_names)
  # REVISIT: @identifying_role_names here are the symbols passed in, not the Role objects we should use.
  # We'd need late binding to use Role objects...
  @identifying_role_names = args if args.size > 0 || !@identifying_role_names
end

#verbaliseObject

verbalise this concept



228
229
230
# File 'lib/activefacts/api/entity.rb', line 228

def verbalise
  "#{basename} is identified by #{identifying_role_names.map{|role_sym| role_sym.to_s.camelcase(true)}*" and "};"
end