Tcl Library Source Code

EuroTcl/OpenACS 11 - 12 JULY 2024, VIENNA

[ Main Table Of Contents | Table Of Contents | Keyword Index | Categories | Modules | Applications ]


clay - A minimalist framework for large scale OO Projects

Table Of Contents


package require Tcl 8.6 9
package require uuid
package require oo::dialect

proc clay::PROC name arglist body ?ninja ____?
proc clay::_ancestors resultvar class
proc clay::ancestors ?args?
proc clay::args_to_dict ?args?
proc clay::args_to_options ?args?
proc clay::dynamic_arguments ensemble method arglist ?args?
proc clay::dynamic_wrongargs_message arglist
proc clay::is_dict d
proc clay::is_null value
proc clay::leaf ?args?
proc clay::K a b
proc clay::noop ?args?
proc clay::cleanup
proc clay::object_create objname ?class ____?
proc clay::object_rename object newname
proc clay::object_destroy ?args?
proc clay::path ?args?
proc clay::putb ?map? text
proc clay::script_path
proc clay::NSNormalize qualname
proc clay::uuid_generate ?args?
proc clay::uuid::generate_tcl_machinfo
proc clay::uuid::tostring uuid
proc clay::uuid::fromstring uuid
proc clay::uuid::equal left right
proc clay::uuid cmd ?args?
proc clay::tree::sanitize dict
proc clay::tree::_sanitizeb path varname dict
proc clay::tree::storage rawpath
proc clay::tree::dictset varname ?args?
proc clay::tree::dictmerge varname ?args?
proc clay::tree::merge ?args?
proc dictargs::proc name argspec body
proc dictargs::method name argspec body
proc clay::dialect::Push class
proc clay::dialect::Peek
proc clay::dialect::Pop
proc clay::dialect::create name ?parent ____?
proc clay::dialect::NSNormalize namespace qualname
proc clay::dialect::DefineThunk target ?args?
proc clay::dialect::Canonical namespace NSpace class
proc clay::dialect::Define namespace class ?args?
proc clay::dialect::Aliases namespace ?args?
proc clay::dialect::SuperClass namespace ?args?
proc clay::dynamic_methods class
proc clay::dynamic_methods_class thisclass
proc clay::define::Array name ?values ____?
proc clay::define::Delegate name info
proc clay::define::constructor arglist rawbody
proc clay::define::Class_Method name arglist body
proc clay::define::class_method name arglist body
proc clay::define::clay ?args?
proc clay::define::destructor rawbody
proc clay::define::Dict name ?values ____?
proc clay::define::Option name ?args?
proc clay::define::Method name argstyle argspec body
proc clay::define::Option_Class name ?args?
proc clay::define::Variable name ?default ____?
proc clay::ensemble_methodbody ensemble einfo
proc clay::define::Ensemble rawmethod ?args?
proc clay::event::cancel self ?task *?
proc clay::event::generate self event ?args?
proc clay::event::nextid
proc clay::event::Notification_list self event ?stackvar ____?
proc clay::event::notify rcpt sender event eventinfo
proc clay::event::process self handle script
proc clay::event::schedule self handle interval script
proc clay::event::subscribe self who event
proc clay::event::unsubscribe self ?args?
proc clay::singleton name script
method clay ancestors
method clay dump
method clay find path ?path...?
method clay get path ?path...?
method clay GET path ?path...?
method clay merge dict ?dict...?
method clay replace dictionary
method clay search path ?path...?
method clay set path ?path...? value
method clay ancestors
method clay cache path value
method clay cget field
method clay delegate ?stub? ?object?
method clay dump
method clay ensemble_map
method clay eval script
method clay evolve
method clay exists path ?path...?
method clay flush
method clay forward method object
method clay get path ?path...?
method clay leaf path ?path...?
method clay merge dict ?dict...?
method clay mixin class ?class...?
method clay mixinmap ?stub? ?classes?
method clay provenance path ?path...?
method clay replace dictionary
method clay search path valuevar isleafvar
method clay source filename
method clay set path ?path...? value
method InitializePublic


Clay introduces a method ensemble to both oo::class and oo::object called clay. This ensemble handles all of the high level interactions within the framework. Clay stores structured data. Clan manages method delegation. Clay has facilities to manage the complex interactions that come about with mixins.

The central concept is that inside of every object and class (which are actually objects too) is a dict called clay. What is stored in that dict is left to the imagination. But because this dict is exposed via a public method, we can share structured data between object, classes, and mixins.

Structured Data

Clay uses a standardized set of method interactions and introspection that TclOO already provides to perform on-the-fly searches. On-the-fly searches mean that the data is never stale, and we avoid many of the sorts of collisions that would arise when objects start mixing in other classes during operation.

The clay methods for both classes and objects have a get and a set method. For objects, get will search through the local clay dict. If the requested leaf is not found, or the query is for a branch, the system will then begin to poll the clay methods of all of the class that implements the object, all of that classes’ ancestors, as well as all of the classes that have been mixed into this object, and all of their ancestors.

Intended branches on a tree end with a directory slash (/). Intended leaves are left unadorned. This is a guide for the tool that builds the search results to know what parts of a dict are intended to be branches and which are intended to be leaves. For simple cases, branch marking can be ignored:

::oo::class create ::foo { }
::foo clay set property/ color blue
::foo clay set property/ shape round

set A [::foo new]
$A clay get property/
{color blue shape round}

$A clay set property/ shape square
$A clay get property/
{color blue shape square}

But when you start storing blocks of text, guessing what field is a dict and what isn’t gets messy:

::foo clay set description {A generic thing of designated color and shape}

$A clay get description
{A generic thing of designated color and shape}

Without a convention for discerning branches for leaves what should have been a value can be accidentally parsed as a dictionary, and merged with all of the other values that were never intended to be merge. Here is an example of it all going wrong:
::oo::class create ::foo { }
# Add description as a leaf
::foo clay set description  {A generic thing of designated color and shape}
# Add description as a branch
::foo clay set description/  {A generic thing of designated color and shape}

::oo::class create ::bar {
  superclass foo
# Add description as a leaf
::bar clay set description  {A drinking establishment of designated color and shape and size}
# Add description as a branch
::bar clay set description/  {A drinking establishment of designated color and shape and size}

set B [::bar new]
# As a leaf we get the value verbatim from he nearest ancestor
$B clay get description
  {A drinking establishment of designated color and shape and size}
# As a branch we get a recursive merge
$B clay get description/
{A drinking establishment of designated color and size thing of}

Clay Dialect

Clay is built using the oo::dialect module from Tcllib. oo::dialect allows you to either add keywords directly to clay, or to create your own metaclass and keyword set using Clay as a foundation. For details on the keywords and what they do, consult the functions in the ::clay::define namespace.

Method Delegation

Method Delegation It is sometimes useful to have an external object that can be invoked as if it were a method of the object. Clay provides a delegate ensemble method to perform that delegation, as well as introspect which methods are delegated in that manner. All delegated methods are marked with html-like tag markings (< >) around them.

::clay::define counter {
  Variable counter 0
  method incr {{howmuch 1}} {
    my variable counter
    incr counter $howmuch
  method value {} {
    my variable counter
    return $counter
  method reset {} {
    my variable counter
    set counter 0
::clay::define example {
  variable buffer
  constructor {} {
    # Build a counter object
    set obj [namespace current]::counter
    ::counter create $obj
    # Delegate the counter
    my delegate <counter> $obj
  method line {text} {
    my <counter> incr
    append buffer $text

set A [example new]
$A line {Who’s line is it anyway?}
$A <counter> value



Class clay::class


Class clay::object

clay::object This class is inherited by all classes that have options.



Sean Woods


Bugs, Ideas, Feedback

This document, and the package it describes, will undoubtedly contain bugs and other problems. Please report such in the category oo of the Tcllib Trackers. Please also report any ideas for enhancements you may have for either package and/or documentation.

When proposing code changes, please provide unified diffs, i.e the output of diff -u.

Note further that attachments are strongly preferred over inlined patches. Attachments can be made by going to the Edit form of the ticket immediately after its creation, and then using the left-most button in the secondary navigation bar.


TclOO, oo


Programming tools


Copyright © 2018 Sean Woods