Index: openacs-4/packages/acs-core-docs/www/object-system-design.html =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-core-docs/www/object-system-design.html,v diff -u -r1.26 -r1.26.2.1 --- openacs-4/packages/acs-core-docs/www/object-system-design.html 16 Feb 2005 00:21:03 -0000 1.26 +++ openacs-4/packages/acs-core-docs/www/object-system-design.html 26 Aug 2005 00:02:30 -0000 1.26.2.1 @@ -1,13 +1,13 @@ -
By Pete Su, Michael Yoon, Richard Li, Rafael Schloming
+By Pete Su, Michael Yoon, Richard Li, Rafael Schloming
OpenACS docs are written by the named authors, and may be edited by OpenACS documentation staff. -Before OpenACS 4, software developers writing OpenACS applications or modules would develop each data model separately. However, many applications built on OpenACS share certain characteristics or require certain common services. Examples of such services include:
User comments
Storage of user-defined or extensible sets of attributes
Access control
General auditing and bookkeeping (e.g. creation date, IP addresses, and @@ -32,21 +32,21 @@ object type (e.g. users) to instances of another object type (e.g. groups).
The next section will explore these facilities in the context of the the -particular programming idioms that we wish to generalize.
Related Links
This design document should be read along with the design documents for the new groups system, subsites and the permissions system
The motivation for most of the facilities in the OpenACS 4 Object Model can be +particular programming idioms that we wish to generalize.
Related Links
This design document should be read along with the design documents for the new groups system, subsites and the permissions system
The motivation for most of the facilities in the OpenACS 4 Object Model can be understood in the context of the 3.x code base and the kinds of programming -idioms that evolved there. These are listed and discussed below.
Object identification is a central mechanism in OpenACS 4. Every application +idioms that evolved there. These are listed and discussed below.
Object identification is a central mechanism in OpenACS 4. Every application
object in OpenACS 4 has a unique ID which is mapped to a row in a central table
-called acs_objects. Developers that wish to use OpenACS 4 services
+called acs_objects
. Developers that wish to use OpenACS 4 services
need only take a few simple steps to make sure that their application objects
appear in this table. The fact that every object has a known unique
identifier means that the core can deal with all objects in a generic way. In
other words, we use object identifiers to enable centralized services in a
global and uniform manner.
Implicit Object Identifiers in OpenACS 3.x
The motivation for implementing general object identifiers comes from
several observations of data models in OpenACS 3.x. Many modules use a
-(user_id, group_id, scope) column-triple for the purpose of
+(user_id, group_id, scope)
column-triple for the purpose of
recording ownership information on objects, for access control. User/groups
-also uses (user_id, group_id) pairs in its
-user_group_map table as a way to identify data associated with a
+also uses (user_id, group_id)
pairs in its
+user_group_map
table as a way to identify data associated with a
single membership relation.
Also, in OpenACS 3.x many utility modules exist that do nothing more than attach some extra attributes to existing application data. For example, general comments maintains a table that maps application "page" @@ -63,23 +63,23 @@ construction of generic application-independent services unnecessarily difficult.
Object Identifiers in OpenACS 4
The OpenACS 4 Object Model defines a single mechanism that applications use to
attach unique identifiers to application data. This identifier is the primary
-key of the acs_objects table. This table forms the core of what
+key of the acs_objects
table. This table forms the core of what
we need to provide generic services like access control, general attribute
storage, general presentation and forms tools, and generalized administrative
interfaces. In addition, the object system provides an API that makes it easy
to create new objects when creating application data. All an application must
do to take advantage of general services in OpenACS 4 is to use the new API to
make sure every object the system is to manage is associated with a row in
-acs_objects. More importantly, if they do this, new services
+acs_objects
. More importantly, if they do this, new services
like general comments can be created without requiring existing applications
-to "hook into" them via new metadata.
Note: Object identifiers are a good example of metadata -in the new system. Each row in acs_objects stores information +to "hook into" them via new metadata.
Note: Object identifiers are a good example of metadata
+in the new system. Each row in acs_objects
stores information
about the application object, but not the application object itself.
This becomes more clear if you skip ahead and look at the SQL schema code
-that defines this table.
Until the implementation of the general permissions system, every OpenACS +that defines this table.
Until the implementation of the general permissions system, every OpenACS application had to manage access control to its data separately. Later on, a notion of "scoping" was introduced into the core data model.
"Scope" is a term best explained by example. Consider some -hypothetical rows in the address_book table:
... | scope | user_id | group_id | ... |
... | user | 123 | � | ... |
... | group | � | 456 | ... |
... | public | � | � | ... |
The first row represents an entry in User 123's personal address book,
+hypothetical rows in the address_book
table:
... | scope | user_id | group_id | ... |
... | user | 123 | ... | |
... | group | 456 | ... | |
... | public | ... |
The first row represents an entry in User 123's personal address book, the second row represents an entry in User Group 456's shared address book, and the third row represents an entry in the site's public address book.
In this way, the scoping columns identify the security context in which a @@ -93,29 +93,29 @@ forum message would probably list a forum topic as its context, and a forum topic might list a subsite as its context. Thus, contexts make it easier to break the site up into security domains according to its natural -structure. An object's context is stored in the context_id -column of the acs_objects table.
We use an object's context to provide a default answer to questions
+structure. An object's context is stored in the context_id
+column of the acs_objects
table.
We use an object's context to provide a default answer to questions regarding access control. Whenever we ask a question of the form "can user X perform action Y on object Z", the OpenACS security model will defer to an object's context if there is no information about user X's permission to perform action Y on object Z.
The context system forms the basis for the rest of the OpenACS access control system, which is described in in two separate documents: one for the permissions system and another for the party groups system. The context system -is also used to implement subsites.
As mentioned above, many OpenACS modules provide extensible data models, and +is also used to implement subsites.
As mentioned above, many OpenACS modules provide extensible data models, and need to use application specific mechanisms to keep track of user defined attributes and to map application data to these attributes. In the past, modules either used user/groups or their own ad hoc data model to provide this functionality.
User/Groups in OpenACS 3.x
The user/group system allowed developers to define group types along with attributes to be stored with each instance of a group type. Each group type could define a helper table that stored attributes on each instance of the group type. This table was called the -"_info" table because the name was generated by -appending _info to the name of the group type.
The user/groups data model also provided the
-user_group_type_member_fields and
-user_group_member_fields tables to define attributes for members
+"_info
" table because the name was generated by
+appending _info
to the name of the group type.
The user/groups data model also provided the
+user_group_type_member_fields
and
+user_group_member_fields
tables to define attributes for members
of groups of a specific type and for members of a specific group,
-respectively. The user_group_member_field_map table stored
-values for both categories of attributes in its field_value
+respectively. The user_group_member_field_map
table stored
+values for both categories of attributes in its field_value
column. These tables allowed developers and users to define custom sets of
attributes to store on groups and group members without changing the data
model at the code level.
Many applications in OpenACS 3.x and earlier used the group type mechanism in
@@ -133,36 +133,36 @@
information. This resulted in core data tables that were too "fat",
containing a hodge podge of unrelated information that should have been
normalized away. The canonical example of this is the explosion of the
-users table in OpenACS 3.x. In addition to being sloppy technically,
+users
table in OpenACS 3.x. In addition to being sloppy technically,
these fat tables have a couple of other problems:
They degrade performance.
Denormalization can make it hard to maintain consistency constraints on the data.
Object subtypes provide a way to factor the data model while still keeping track of the fact that each member of a subtype (i.e. for each row in the subtype's table), is also a member of the parent type (i.e. there is a corresponding row in the parent type table). Therefore, applications an use this mechanism without worrying about this bookkeeping themselves, and we avoid having applications pollute the core data model with their specific -information.
As we described above, the OpenACS 3.x user/groups system stored object +information.
As we described above, the OpenACS 3.x user/groups system stored object attributes in two ways. The first was to use columns in the helper table. The second consisted of two tables, one describing attributes and one storing values, to provide a flexible means for attaching attributes to metadata objects. This style of attribute storage is used in several other parts of OpenACS 3.x, and we will refer to it as "skinny tables". For -example:
In the Ecommerce data model, the ec_custom_product_fields +example:
In the Ecommerce data model, the ec_custom_product_fields
table defines attributes for catalog products, and the
-ec_custom_product_field_values table stores values for those
-attributes.
In the Photo DB data model, the ph_custom_photo_fields table
+ec_custom_product_field_values
table stores values for those
+attributes.
In the Photo DB data model, the ph_custom_photo_fields
table
defines attributes for the photographs owned by a specific user, and tables
named according to the convention
-"ph_user_<user_id>_custom_info" are used to
+"ph_user_<user_id>_custom_info
" are used to
store values for those attributes.
In addition, there are some instances where we are not using this model
-but should, e.g. the users_preferences table, which
+but should, e.g. the users_preferences
table, which
stores preferences for registered users in columns such as
-prefer_text_only_p and dont_spam_me_p. The
+prefer_text_only_p
and dont_spam_me_p
. The
"standard" way for an OpenACS 3.x-based application to add to the list
-of user preferences is to add a column to the users_preferences
+of user preferences is to add a column to the users_preferences
table (exactly the kind of data model change that has historically
complicated the process of upgrading to a more recent OpenACS version).
The Objet Model generalizes the scheme used in the old OpenACS 3.x user/groups
-system. It defines a table called acs_attributes that record
+system. It defines a table called acs_attributes
that record
what attributes belong to which object types, and how the attributes are
stored. As before, attributes can either be stored in helper tables, or in a
single central skinny table. The developer makes this choice on a case by
@@ -172,19 +172,19 @@
skinny tables because doing so allows developers and users to dynamically
update the set of attributes stored on an object without updating the data
model at the code level. The bottom line: Helper tables are more functional
-and more efficient, skinny tables are more flexible but limited.
Many OpenACS 3.x modules use mapping tables to model relationships +and more efficient, skinny tables are more flexible but limited.
Many OpenACS 3.x modules use mapping tables to model relationships
between application objects. Again, the 3.x user/groups system provides the
canonical example of this design style. In that system, there was a single
-table called user_group_map that kept track of which users
+table called user_group_map
that kept track of which users
belonged to what groups. In addition, as we discussed in the previous
-section, the system used the user_group_member_fields and
-user_group_member_fields_map tables to allow developers to
+section, the system used the user_group_member_fields
and
+user_group_member_fields_map
tables to allow developers to
attach custom attributes to group members. In fact, these attributes were not
really attached to the users, but to the fact that a user was a member of a
particular group - a subtle but important distinction.
In OpenACS 4, relation types generalize this mechanism. Relation
types allow developers to define general mappings from objects of a given
type T, to other objects of a given type R. Each relation type is a subtype
-of acs_object, extended with extra attributes that store
+of acs_object
, extended with extra attributes that store
constraints on the relation, and the types of objects the relation actually
maps. In turn, each instance of a relation type is an object that represents
a single fact of the form "the object t of type T is related to the
@@ -203,9 +203,9 @@
object system could use the meta data in the types table to do useful things
in a generic way on all relation types. But this mechanism doesn't really
exist yet.
Relation types are a somewhat abstract idea. To get a better feel for -them, you should just skip to the data model.
The OpenACS 4 Object Model is designed to generalize and unify the following +them, you should just skip to the data model.
The OpenACS 4 Object Model is designed to generalize and unify the following mechanisms that are repeatedly implemented in OpenACS-based systems to manage -generic and application specific metadata:
The presence of a framework for subtyping and inheritance always brings up +generic and application specific metadata:
The presence of a framework for subtyping and inheritance always brings up the question of why we don't just use an object database. The main reason is that all of the major object database vendors ship products that are effectively tied to some set of object oriented programming languages. Their @@ -229,7 +229,7 @@ practice. Finally, object databases are not as widely used as traditional relational systems. They have not been tested as extensively and their scalability to very large databases is not proven (though some will disagree -with this statement).
The conclusion: the best design is to add a limited notion of subtyping to +with this statement).
The conclusion: the best design is to add a limited notion of subtyping to our existing relational data model. By doing this, we retain all the power of the relational data model while gaining the object oriented features we need most.
In the context of OpenACS 4, this means using the object model to make our @@ -240,9 +240,9 @@ the more limited domain of the metadata model, this is acceptable since the type hierarchy is fairly small. But the object system data model is not designed to support, for example, a huge type tree like the Java runtime -libraries might define.
This last point cannot be over-stressed: the object model is not -meant to be used for large scale application data storage. It is -meant to represent and store metadata, not application data.
Like most data models, the OpenACS Core data model has two levels:
The knowledge level (i.e. the metadata model)
The operational level (i.e. the concrete data model)
+libraries might define.
This last point cannot be over-stressed: the object model is not +meant to be used for large scale application data storage. It is +meant to represent and store metadata, not application data.
Like most data models, the OpenACS Core data model has two levels:
The knowledge level (i.e. the metadata model)
The operational level (i.e. the concrete data model)
You can browse the data models themselves from here:
The knowledge level data model for OpenACS objects centers around three tables +and things like constraint names are not included.
The knowledge level data model for OpenACS objects centers around three tables
that keep track of object types, attributes, and relation types. The first
-table is acs_object_types, shown here in an abbreviated
+table is acs_object_types
, shown here in an abbreviated
form:
-create table acs_object_types (
+create table acs_object_types (
object_type varchar(100) not null primary key,
supertype references acs_object_types (object_type),
abstract_p char(1) default 'f' not null
@@ -269,26 +269,26 @@
name_method varchar(30),
type_extension_table varchar(30)
);
-
+
This table contains one row for every object type in the system. The key things to note about this table are:
For every type, we store metadata for how to display this type in certain -contexts (pretty_name and pretty_plural).
If the type is a subtype, then its parent type is stored in the column -supertype.
We support a notion of "abstract" types that contain no
+contexts (pretty_name
and pretty_plural
).
If the type is a subtype, then its parent type is stored in the column
+supertype
.
We support a notion of "abstract" types that contain no instances (as of 9/2000 this is not actually used). These types exist only to be subtyped. An example might be a type representing "shapes" that contains common characteristics of all shapes, but which is only used to create subtypes that represent real, concrete shapes like circles, squares, and so on.
Every type defines a table in which one can find one row for every -instance of this type (table_name, id_column).
type_extension_table is for naming a table that stores extra -generic attributes.
The second table we use to describe types is acs_attributes.
+instance of this type (table_name
, id_column
).
type_extension_table
is for naming a table that stores extra
+generic attributes.
The second table we use to describe types is acs_attributes
.
Each row in this table represents a single attribute on a specific object
type (e.g. the "password" attribute of the "user" type).
Again, here is an abbreviated version of what this table looks like. The
actual table used in the implementation is somewhat different and is
discussed in a separate document.
-create table acs_attributes (
+create table acs_attributes (
attribute_id integer not null primary key
object_type not null references acs_object_types (object_type),
attribute_name varchar(100) not null,
@@ -304,25 +304,25 @@
max_n_values integer default 1 not null,
static_p varchar(1)
);
-
+
The following points are important about this table:
Every attribute has a unique identifier.
Every attribute is associated with an object type.
We store various things about each attribute for presentation -(pretty_name, sort_order).
The data_type column stores type information on this
+(pretty_name
, sort_order
).
The data_type
column stores type information on this
attribute. This is not the SQL type of the attribute; it is just a human
readable name for the type of data we think the attribute holds (e.g.
"String", or "Money"). This might be used later to
-generate a user interface.
The sort_order column stores information about how to sort +generate a user interface.
The sort_order
column stores information about how to sort
the attribute values.
Attributes can either be stored explicitly in a table ("type
specific storage") or in a skinny table ("generic storage").
In most cases, an attribute maps directly to a column in the table identified
-by the table_name of the corresponding object type, although, as
+by the table_name
of the corresponding object type, although, as
mentioned above, we sometimes store attribute values as key-value pairs in a
"skinny" table. However, when you ask the question "What are
the attributes of this type of object?", you don't really care about
how the values for each attribute are stored (in a column or as key-value
-pairs); you expect to receive the complete list of all attributes.
The max_n_values and min_n_values columns +pairs); you expect to receive the complete list of all attributes.
The max_n_values
and min_n_values
columns
encode information about the number of values an attribute may hold.
-Attributes can be defined to hold 0 or more total values.
The static_p flag indicates whether this attribute value is +Attributes can be defined to hold 0 or more total values.
The static_p
flag indicates whether this attribute value is
shard by all instances of a type, as with static member fields in C++. Static
attribute are like group level attributes in OpenACS 3.x.
The final part of the knowledge level model keeps track of relationship types. We said above that object relationships are used to generalize the 3.x @@ -333,9 +333,9 @@ to the user. This is a subtle but important distinction, because it allowed the 3.x system to store multiple sets of attributes on a given user, one set for each group membership relation in which they participated.
In OpenACS 4, this sort of data can be stored as a relationship type, in -acs_rel_types. The key parts of this table look like this:
+acs_rel_types
. The key parts of this table look like this:-create table acs_rel_types ( +create table acs_rel_types ( rel_type varchar(100) not null references acs_object_types(object_type), object_type_one not null @@ -349,31 +349,31 @@ min_n_rels_two integer default 0 not null, max_n_rels_two integer ); -
+Things to note about this table:
The main part of this table records the fact that the relation is between -instances of object_type_one and instances of -object_type_two. Therefore, each instance of this relation type -will be a pair of objects of the appropriate types.
The role columns store human readable names for the roles +instances of
object_type_one
and instances of +object_type_two
. Therefore, each instance of this relation type +will be a pair of objects of the appropriate types.The
role
columns store human readable names for the roles played by each object in the relation (e.g. "employee" and "employer"). Each role must appear in the -acs_rel_roles.The min_n_rels_one column, and its three friends allow the +
acs_rel_roles
.The
min_n_rels_one
column, and its three friends allow the programmer to specify constraints on how many objects any given object can be -related to on either side of the relation.This table is easier to understand if you also know how the acs_rels table works.
To summarize, the acs_object_types and -acs_attributes tables store metadata that describes every object +related to on either side of the relation.
This table is easier to understand if you also know how the acs_rels
table works.
To summarize, the acs_object_types
and
+acs_attributes
tables store metadata that describes every object
type and attribute in the system. These tables generalize the group types
-data model in OpenACS 3.x. The acs_rel_types table stores
+data model in OpenACS 3.x. The acs_rel_types
table stores
information about relation types.
This part of the data model is somewhat analogous to the data dictionary in Oracle. The information stored here is primarily metadata that describes the data stored in the operational level of the data -model, which is discussed next.
The operational level data model centers around the -acs_objects table. This table contains a single row for every -instance of the type acs_object. The table contains the +model, which is discussed next.
The operational level data model centers around the
+acs_objects
table. This table contains a single row for every
+instance of the type acs_object
. The table contains the
object's unique identifier, a reference to its type, security
information, and generic auditing information. Here is what the table looks
like:
-create table acs_objects (
+create table acs_objects (
object_id integer not null,
object_type not null
references acs_object_types (object_type),
@@ -387,32 +387,32 @@
modifying_user integer,
modifying_ip varchar(50)
);
-
+
As we said in Section III, security contexts are hierarchical and also modeled as objects. There is another table called -acs_object_context_index that stores the context hierarchy.
Other tables in the core data model store additional information related
-to objects. The table acs_attribute_values and
-acs_static_attr_values are used to store attribute values that
+acs_object_context_index
that stores the context hierarchy.
Other tables in the core data model store additional information related
+to objects. The table acs_attribute_values
and
+acs_static_attr_values
are used to store attribute values that
are not stored in a helper table associated with the object's type. The
former is used for instance attributes while the latter is used for
class-wide "static" values. These tables have the same basic form,
so we'll only show the first:
-create table acs_attribute_values (
+create table acs_attribute_values (
object_id not null
references acs_objects (object_id) on delete cascade,
attribute_id not null
references acs_attributes (attribute_id),
attr_value varchar(4000),
primary key (object_id, attribute_id)
);
-
+
-
Finally, the table acs_rels is used to store object pairs +
Finally, the table acs_rels
is used to store object pairs
that are instances of a relation type.
-create table acs_rels (
+create table acs_rels (
rel_id not null
references acs_objects (object_id)
primary key
@@ -424,33 +424,33 @@
references acs_objects (object_id),
unique (rel_type, object_id_one, object_id_two)
);
-
+
-
This table is somewhat subtle:
rel_id is the ID of an instance of some relation +
This table is somewhat subtle:
rel_id
is the ID of an instance of some relation
type. We do this so we can store all the mapping tables in this one
-table.
rel_type is the ID of the relation type to which this object +table.
rel_type
is the ID of the relation type to which this object
belongs.
The next two object IDs are the IDs of the objects being mapped.
All this table does is store one row for every pair of objects that we'd like to attach with a relation. Any additional attributes that we'd like to attach to this pair of objects is specified in the attributes of the relation type, and could be stored in any number of places. As in the 3.x user/groups system, these places include helper tables or -generic skinny tables.
This table, along with acs_attributes and -acs_attribute_values generalize the old user/group tables -user_group_map, user_group_member_fields_map and -user_group_member_fields.
The core tables in the OpenACS 4 data model store information about instances -of object types and relation types. The acs_object table +generic skinny tables.
This table, along with acs_attributes
and
+acs_attribute_values
generalize the old user/group tables
+user_group_map
, user_group_member_fields_map
and
+user_group_member_fields
.
The core tables in the OpenACS 4 data model store information about instances
+of object types and relation types. The acs_object
table
provides the central location that contains a single row for every object in
the system. Services can use this table along with the metadata in stored in
the knowledge level data model to create, manage, query and manipulate
-objects in a uniform manner. The acs_rels table has an analogous
+objects in a uniform manner. The acs_rels
table has an analogous
role in storing information on relations.
These are all the tables that we'll discuss in this document. The rest of the Kernel data model is described in the documents for subsites, the permissions system and for the groups system.
Some examples of how these tables are used in the system can be found in -the discussion of the API, which comes next.
Now we'll examine each piece of the API in detail. Bear in mind that -the Object Model API is defined primarily through PL/SQL packages.
The object system provides an API for creating new object types and then -attaching attributes to them. The procedures create_type and -drop_type are used to create and delete type definitions.
The two calls show up in the package acs_object_type.
+the discussion of the API, which comes next.
Now we'll examine each piece of the API in detail. Bear in mind that +the Object Model API is defined primarily through PL/SQL packages.
The object system provides an API for creating new object types and then
+attaching attributes to them. The procedures create_type
and
+drop_type
are used to create and delete type definitions.
The two calls show up in the package acs_object_type
.
- procedure create_type (
+ procedure create_type (
object_type in acs_object_types.object_type%TYPE,
pretty_name in acs_object_types.pretty_name%TYPE,
pretty_plural in acs_object_types.pretty_plural%TYPE,
@@ -469,13 +469,13 @@
object_type in acs_object_types.object_type%TYPE,
cascade_p in char default 'f'
);
-
+
-
Here the cascade_p argument indicates whether dropping a type +
Here the cascade_p
argument indicates whether dropping a type
should also remove all its subtypes from the system.
We define a similar interface for defining attributes in the package -acs_attribute:
+acs_attribute
:- function create_attribute ( +function create_attribute ( object_type in acs_attributes.object_type%TYPE, attribute_name in acs_attributes.attribute_name%TYPE, datatype in acs_attributes.datatype%TYPE, @@ -496,12 +496,12 @@ attribute_name in varchar ); -
+In addition, the following two calls are available for attaching extra annotations onto attributes:
- procedure add_description ( +procedure add_description ( object_type in acs_attribute_descriptions.object_type%TYPE, attribute_name in acs_attribute_descriptions.attribute_name%TYPE, description_key in acs_attribute_descriptions.description_key%TYPE, @@ -513,30 +513,30 @@ attribute_name in acs_attribute_descriptions.attribute_name%TYPE, description_key in acs_attribute_descriptions.description_key%TYPE ); -
+At this point, what you must do to hook into the object system from your -own data model becomes clear:
Create a table that will store the instances of the new type.
Call acs_object_type.create_type() to fill in the metadata +own data model becomes clear:
Create a table that will store the instances of the new type.
Call
acs_object_type.create_type()
to fill in the metadata table on this new type. If you want your objects to appear in the -acs_objects table, then your new type must be a subtype of -acs_object.Call acs_attribute.create_attribute() to fill in information +
acs_objects
table, then your new type must be a subtype of +acs_object
.Call
acs_attribute.create_attribute()
to fill in information on the attributes that this type defines.So, suppose we are writing a new version of the ticket tracker for 4.0. We probably define a table to store tickets in, and each ticket might have an ID and a description. If we want each ticket to be an object, then -ticket_id must reference the object_id column in -acs_objects:
+ticket_id
must reference theobject_id
column in +acs_objects
:-create table tickets ( +create table tickets ( ticket_id references acs_objects (object_id), description varchar(512), ... ) ; -
+In addition to defining the table, we need this extra PL/SQL code to hook into the object type tables:
-declare +declare attr_id acs_attributes.attribute_id%TYPE; begin acs_object_type.create_type ( @@ -561,25 +561,25 @@ commit; end; -
+Thus, with a small amount of extra code, the new ticket tracker will now automatically be hooked into every generic object service that exists. Better still, this code need not be changed as new services are added. As an aside, the most important service that requires you to subtype -acs_object is permissions.
The next important piece of the API is defined in the -acs_object package, and is concerned with creating and managing +
acs_object
is permissions.The next important piece of the API is defined in the +
acs_object
package, and is concerned with creating and managing objects. This part of the API is designed to take care of the mundane bookkeeping needed to create objects and query their attributes. Realistically however, limitations in PL/SQL and Oracle will make it hard to build generic procedures for doing large scale queries in the object system, so developers who need to do this will probably have to be fairly familiar -with the data model at a lower level.The function acs_object.new() makes a new object for you. The -function acs_object.del() deletes an object. As before, this +with the data model at a lower level.
The function
acs_object.new()
makes a new object for you. The +functionacs_object.del()
deletes an object. As before, this is an abbreviated interface with all the long type specs removed. See the data model or developer's guide for the full interface.- function new ( +function new ( object_id in acs_objects.object_id%TYPE default null, object_type in acs_objects.object_type%TYPE default 'acs_object', @@ -594,28 +594,28 @@ procedure delete ( object_id in acs_objects.object_id%TYPE ); -
+Next, we define some generic functions to manipulate attributes. Again, these interfaces are useful to an extent, but for large scale queries, it's likely that developers would have to query the data model directly, -and then encapsulate their queries in procedures.
For names, the default_name function is used if you don't +and then encapsulate their queries in procedures.
For names, the
default_name
function is used if you don't want to define your own name function.- function name ( +function name ( object_id in acs_objects.object_id%TYPE ) return varchar; function default_name ( object_id in acs_objects.object_id%TYPE ) return varchar; -
+The following functions tell you where attributes are stored, and fetch single attributes for you.
- procedure get_attribute_storage ( +procedure get_attribute_storage ( object_id_in in acs_objects.object_id%TYPE, attribute_name_in in acs_attributes.attribute_name%TYPE, v_column out varchar2, @@ -633,17 +633,17 @@ attribute_name_in in acs_attributes.attribute_name%TYPE, value_in in varchar2 ); -
+ -The main use of the acs_object package is to create +
The main use of the
acs_object
package is to create application objects and make them available for services via the -acs_objects table. To do this, you just have to make sure you -call acs_object.new() on objects that you wish to appear in the -acs_objects table. In addition, all such objects must be -instances of some subtype of acs_object.Continuing the ticket example, we might define the following sort of +
acs_objects
table. To do this, you just have to make sure you +callacs_object.new()
on objects that you wish to appear in the +acs_objects
table. In addition, all such objects must be +instances of some subtype ofacs_object
.Continuing the ticket example, we might define the following sort of procedure for creating a new ticket:
- function new_ticket ( +function new_ticket ( package_id in tickets.ticket_id%TYPE default null, description in tickets.description%TYPE default '', @@ -663,42 +663,42 @@ (v_ticket_id, description); return v_ticket_id; end new_ticket; -
+This function will typically be defined in the context of a PL/SQL package, but we've left it stand-alone here for simplicity.
To summarize: in order to take advantage of OpenACS 4 services, a new application need only do three things:
Define a data model to describe application objects. This can just be a normal SQL table.
Create an object type, using code like in the example from the previous section.
Make sure application objects are created using -acs_object.new() in addition to whatever SQL code is needed to +
acs_object.new()
in addition to whatever SQL code is needed to insert a new row into the application data model.One of the design goals of OpenACS 4 was to provide a straightforward and consistent mechanism to provide applications with general services. What we have seen here is that three simple steps and minimal changes in the application data model are sufficient to make sure that application objects -are represented in the acs_objects table. Subsequently, all of +are represented in the
acs_objects
table. Subsequently, all of the general services in OpenACS 4 (i.e. permissions, general comments, and so on) -are written to work with any object that appears in acs_objects. +are written to work with any object that appears inacs_objects
. Therefore, in general these three steps are sufficient to make OpenACS 4 services -available to your application.The relations system defines two packages: acs_rel_type for -creating and managing relation types, and acs_rel for relating +available to your application.
The relations system defines two packages:
acs_rel_type
for +creating and managing relation types, andacs_rel
for relating objects.These two procedures just insert and remove roles from the -acs_rel_roles table. This table stores the legal relationship +
acs_rel_roles
table. This table stores the legal relationship "roles" that can be used when creating relation types. Examples of roles are, say, "member", or "employer".- procedure create_role ( +procedure create_role ( role in acs_rel_roles.role%TYPE ); procedure drop_role ( role in acs_rel_roles.role%TYPE ); -
+ -The main functions in the acs_rel_type package are used to +
The main functions in the
acs_rel_type
package are used to create and drop relation types.- procedure create_type ( +procedure create_type ( rel_type in acs_rel_types.rel_type%TYPE, pretty_name in acs_object_types.pretty_name%TYPE, pretty_plural in acs_object_types.pretty_plural%TYPE, @@ -724,12 +724,12 @@ rel_type in acs_rel_types.rel_type%TYPE, cascade_p in char default 'f' ); -
+ -Finally, the acs_rel package provides an API that you use to +
Finally, the
acs_rel
package provides an API that you use to create and destroy instances of a relation type:- function new ( +function new ( rel_id in acs_rels.rel_id%TYPE default null, rel_type in acs_rels.rel_type%TYPE default 'relationship', object_id_one in acs_rels.object_id_one%TYPE, @@ -742,15 +742,15 @@ procedure delete ( rel_id in acs_rels.rel_id%TYPE ); -
+A good example of how to use relation types appears in the OpenACS 4 data model for groups. As in 3.x, group membership is modeled using a mapping table, but now we create this mapping using relation types instead of explicitly creating a table. First, we create a helper table to store state on each membership fact:
-create table membership_rels ( +create table membership_rels ( rel_id constraint membership_rel_rel_id_fk references acs_rels (rel_id) constraint membership_rel_rel_id_pk @@ -760,11 +760,11 @@ check (member_state in ('approved', 'banned', 'rejected', 'deleted')) ); -
+Then, we create a new object type to describe groups.
- acs_object_type.create_type ( +acs_object_type.create_type ( object_type => 'group', pretty_name => 'Group', pretty_plural => 'Groups', @@ -773,18 +773,18 @@ type_extension_table => 'group_types', name_method => 'acs_group.name' ); -
+In this example, we've made groups a subtype of -acs_object to make the code simpler. The actual data model is +
acs_object
to make the code simpler. The actual data model is somewhat different. Also, we've assumed that there is a helper table -called groups to store information on groups, and that there is -a helper table called group_types that has been defined to store -extra attributes on groups.Now, assuming we have another object type called person to +called
groups
to store information on groups, and that there is +a helper table calledgroup_types
that has been defined to store +extra attributes on groups.Now, assuming we have another object type called
person
to represent objects that can be group members, we define the following relationship type for group membership:- acs_rel_type.create_role ('member'); +acs_rel_type.create_role ('member'); acs_rel_type.create_type ( rel_type => 'membership_rel', @@ -797,16 +797,16 @@ object_type_two => 'person', role_two => 'member', min_n_rels_two => 0, max_n_rels_two => null ); -
+Now we can define the following procedure to add a new member to a group. All this function does is create a new instance of the membership relation type and then insert the membership state into the helper table that we define above. In the actual implementation, this function is implemented in -the membership_rel package. Here we just define an independent +the
membership_rel
package. Here we just define an independent function:-function member_add ( +function member_add ( rel_id in membership_rels.rel_id%TYPE default null, rel_type in acs_rels.rel_type%TYPE default 'membership_rel', group in acs_rels.object_id_one%TYPE, @@ -833,12 +833,12 @@ value (v_rel_id, new.member_state); end; -
+Another simple function can be defined to remove a member from a group:
- procedure member_delete ( +procedure member_delete ( rel_id in membership_rels.rel_id%TYPE ) is @@ -848,13 +848,13 @@ acs_rel.del(rel_id); end; -
+ -The Object Model's API and data model provides a small set of simple procedures that allow applications to create object types, object instances, and object relations. Most of the data model is straightforward; the relation type mechanism is a bit more complex, but in return it provides functionality -on par with the old user/groups system in a more general way.
Pete Su generated this document from material culled from other documents by Michael Yoon, Richard Li and Rafael Schloming. But, any remaining lies -are his and his alone.
Document Revision # | Action Taken, Notes | When? | By Whom? | ||||||||||||||
0.1 | Creation | 9/09/2000 | Pete Su | ||||||||||||||
0.2 | Edited for ACS 4 Beta | 9/30/2000 | Kai Wu | ||||||||||||||
0.3 | Edited for ACS 4.0.1, fixed some mistakes, removed use of term
+are his and his alone.
|