Module: ActiveFacts::API::ObjectType

Included in:
Instance::ClassMethods
Defined in:
lib/activefacts/api/object_type.rb

Overview

ObjectType contains methods that are added as class methods to all Value and Entity classes.

Instance Method Summary collapse

Instance Method Details

#add_role(role) ⇒ Object



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

def add_role(role)
	all_role[role.name] = role
	@all_role_transitive = nil  # Undo the caching
end

#all_role(role_name = nil) ⇒ Object

Each ObjectType maintains a list of the Roles it plays:



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/activefacts/api/object_type.rb', line 20

def all_role(role_name = nil)
  unless instance_variable_defined?(@@all_role_name ||= "@all_role")  # Avoid "instance variable not defined" warning from ||=
    @all_role = RoleCollection.new
  end
  case role_name
  when nil
    @all_role
  when Symbol, String
    # Search this class then all supertypes:
    unless role = @all_role[role_name.to_sym]
      role = nil
      supertypes.each do |supertype|
          begin
            role = supertype.all_role(role_name)
          rescue RoleNotDefinedException
            next
          end
          break
        end
    end
    unless role
      raise RoleNotDefinedException.new(self, role_name)
    end
    role
  else
    nil
  end
end

#all_role_transitiveObject



54
55
56
57
58
59
60
61
# File 'lib/activefacts/api/object_type.rb', line 54

def all_role_transitive
	return @all_role_transitive if @all_role_transitive
	@all_role_transitive = all_role.dup
	supertypes_transitive.each do |klass|
	  @all_role_transitive.merge!(klass.all_role)
	end
	@all_role_transitive
end

#check_identifying_role_has_valid_cardinality(type, role) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/activefacts/api/object_type.rb', line 105

def check_identifying_role_has_valid_cardinality(type, role)
  if is_entity_type && identifying_role_names.include?(role)
    case type
    when :has_one
      if identifying_role_names.size == 1
 raise InvalidIdentificationException.new(self, role, true)
      end
    when :one_to_one
      if identifying_role_names.size > 1
 raise InvalidIdentificationException.new(self, role, false)
      end
    end
  end
end

#has_one(role_name, options = {}) ⇒ Object

Define a binary fact type relating this object_type to another, with a uniqueness constraint only on this object_type’s role. This method creates two accessor methods, one in this object_type and one in the other object_type.

  • role_name is a Symbol for the name of the role (this end of the relationship)

Options contain optional keys:

  • :class - A class name, Symbol or String naming a class, required if it doesn’t match the role_name. Use a symbol or string if the class isn’t defined yet, and the methods will be created later, when the class is first defined.

  • :mandatory - if this role may not be NULL in a valid fact population, say :mandatory => true. Mandatory constraints are only enforced during validation (e.g. before saving).

  • :counterpart - use if the role at the other end should have a name other than the default :all_<object_type> or :all_<object_type>_as_<role_name>

  • :reading - for verbalisation. Not used yet.

  • :restrict - a list of values or ranges which this role may take. Not used yet.



82
83
84
85
86
# File 'lib/activefacts/api/object_type.rb', line 82

def has_one(role_name, options = {})
  role_name, related, mandatory, related_role_name = extract_binary_params(false, role_name, options)
  check_identifying_role_has_valid_cardinality(:has_one, role_name)
  define_binary_fact_type(false, role_name, related, mandatory, related_role_name)
end

#maybe(role_name, options = {}) ⇒ Object

Define a unary fact type attached to this object_type; in essence, a boolean attribute.

Example: maybe :is_ceo



66
67
68
69
70
# File 'lib/activefacts/api/object_type.rb', line 66

def maybe(role_name, options = {})
	raise UnrecognisedOptionsException.new("role", role_name, options.keys) unless options.empty?
	fact_type = FactType.new
  realise_role(Role.new(fact_type, self, role_name, false, true))
end

#one_to_one(role_name, options = {}) ⇒ Object

Define a binary fact type joining this object_type to another, with uniqueness constraints in both directions, i.e. a one-to-one relationship This method creates two accessor methods, one in this object_type and one in the other object_type.

  • role_name is a Symbol for the name of the role (this end of the relationship)

Options contain optional keys:

  • :class - A class name, Symbol or String naming a class, required if it doesn’t match the role_name. Use a symbol or string if the class isn’t defined yet, and the methods will be created later, when the class is first defined.

  • :mandatory - if this role may not be NULL in a valid fact population, say :mandatory => true. Mandatory constraints are only enforced during validation (e.g. before saving).

  • :counterpart - use if the role at the other end should have a name other than the default :all_<object_type> or :all_<object_type>_as_<role_name>

  • :reading - for verbalisation. Not used yet.

  • :restrict - a list of values or ranges which this role may take. Not used yet.



98
99
100
101
102
103
# File 'lib/activefacts/api/object_type.rb', line 98

def one_to_one(role_name, options = {})
  role_name, related, mandatory, related_role_name =
    extract_binary_params(true, role_name, options)
  check_identifying_role_has_valid_cardinality(:one_to_one, role_name)
  define_binary_fact_type(true, role_name, related, mandatory, related_role_name)
end

#realise_role(role) ⇒ Object

Every new role added or inherited comes through here:



176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/activefacts/api/object_type.rb', line 176

def realise_role(role) #:nodoc:
  if (role.unary?)
    # Unary role
    define_unary_role_accessor(role)
  elsif (role.unique)
    if role.counterpart.unique
      define_one_to_one_accessor(role)
    else
      define_one_to_many_accessor(role)
    end
  else
    define_many_to_one_accessor(role)
  end
end

#subtypesObject



171
172
173
# File 'lib/activefacts/api/object_type.rb', line 171

def subtypes
  @subtypes ||= []
end

#supertypes(*object_types) ⇒ Object

Access supertypes or add new supertypes; multiple inheritance. With parameters (Class objects), it adds new supertypes to this class. Instances of this class will then have role methods for any new superclasses (transitively). Superclasses must be Ruby classes which are existing ObjectTypes. Without parameters, it returns the array of ObjectType supertypes (one by Ruby inheritance, any others as defined using this method)



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/object_type.rb', line 126

def supertypes(*object_types)
	@supertypes ||= []
	all_supertypes = supertypes_transitive
	object_types.each do |object_type|
	  next if all_supertypes.include? object_type
	  supertype =
	    case object_type
	    when Class
 object_type
	    when Symbol
 # No late binding here:
 (object_type = vocabulary.const_get(object_type.to_s.camelcase))
	    else
 raise InvalidSupertypeException.new("Illegal supertype #{object_type.inspect} for #{self.class.basename}")
	    end
	  unless supertype.respond_to?(:vocabulary) and supertype.vocabulary == self.vocabulary
	    raise InvalidSupertypeException.new("#{supertype.name} must be an object type in #{vocabulary.name}")
	  end

	  if is_entity_type != supertype.is_entity_type
	    raise InvalidSupertypeException.new("#{self} < #{supertype}: A value type may not be a supertype of an entity type, and vice versa")
	  end

	  TypeInheritanceFactType.new(supertype, self)
	  @supertypes << supertype

	  # Realise the roles (create accessors) of this supertype.
	  realise_supertypes(object_type, all_supertypes)
	end
	[(superclass.respond_to?(:vocabulary) ? superclass : nil), *@supertypes].compact
end

#supertypes_transitiveObject

Return the array of all ObjectType supertypes, transitively.



159
160
161
162
163
164
165
166
167
168
169
# File 'lib/activefacts/api/object_type.rb', line 159

def supertypes_transitive
	supertypes = []
	v = superclass.respond_to?(:vocabulary) ? superclass.vocabulary : nil
	supertypes << superclass if v.kind_of?(Module)
	supertypes += (@supertypes ||= [])
	sts = supertypes.inject([]) do |a, t|
	  next if a.include?(t)
	  a += [t] + t.supertypes_transitive
	end.uniq
	sts # The local variable unconfuses rcov
end

#vocabularyObject

What vocabulary (Ruby module) does this object_type belong to?



15
16
17
# File 'lib/activefacts/api/object_type.rb', line 15

def vocabulary
  modspace        # The module that contains this object_type.
end