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



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

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.



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

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
# 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?
  realise_role(roles[role_name] = Role.new(self, TrueClass, role_name))
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.



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

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:



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

def realise_role(role) #:nodoc:
  if (role.is_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



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

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)



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

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.



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

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