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

#all_rolesObject



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

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

#check_identifying_role_has_valid_cardinality(type, role) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/activefacts/api/object_type.rb', line 100

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.



77
78
79
80
81
# File 'lib/activefacts/api/object_type.rb', line 77

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



61
62
63
64
65
# File 'lib/activefacts/api/object_type.rb', line 61

def maybe(role_name, options = {})
	raise UnrecognisedOptionsException.new("role", role_name, options.keys) unless options.empty?
	fact_type = FactType.new
  realise_role(roles[role_name] = 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.



93
94
95
96
97
98
# File 'lib/activefacts/api/object_type.rb', line 93

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:



172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/activefacts/api/object_type.rb', line 172

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

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

#subtypesObject



167
168
169
# File 'lib/activefacts/api/object_type.rb', line 167

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)



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

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

	  @supertypes << supertype

	  # Realise the roles (create accessors) of this supertype.
	  # REVISIT: The existing accessors at the other end will need to allow this class as role counterpart
	  # REVISIT: Need to check all superclass roles recursively, unless we hit a common 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.



155
156
157
158
159
160
161
162
163
164
165
# File 'lib/activefacts/api/object_type.rb', line 155

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