]

# The Clay Framework

Sean Woods

The Clay Framework is an attempt to allow prototype based programming within TclOO.
Without resorting to event more heroic means

# What is clay used for?

Clay introduces a Tcl-ish take on prototype based programming.

- The meta-class does virtually nothing, except know how to bolt on more functions through mixins.
- As the object reads it's configuration it uses mixins to add new behavior.
- The object then sets out to accomplish its task.

# Applications

Dynamic web content |
A Tcl based make system |
Gaming/Chatbots/AI |

# Applications

Dynamic web content | Httpd / Toadhttpd
A Tcl based make system | Practcl
Gaming/Chatbots/AI | The Epic of Gilgamesh (Under Development)

# Clay in Httpd

httpd::reply only provides basic MIME parsing and socket wrangling.

- Parts of that socket wrangling are swappable to implement proxies.
- Custom content replaces the content method.
- Large sites can swap out the header/footer methods to provide sitewide navigation, inline style sheets, Bootstrap/jQuery integration

# Clay in Httpd

httpd::server utilizes mixins:

- Controlled through the plugin method
- Used to swap out dispatch engines, logging mechanisms, and security lockdowns.

# Rest API Example

    HTTPD uri direct * restapi {} {
      # Have the reply do its own dispatching
      set URI [split [my request get REQUEST_URI] /]
      lassign [split $URI] base method uuid
      set class [find_class $method]
      if {$class eq {}} {
        # Errors will turned into the appropriate HTTP reply
        tailcall my error 404 {Not Found} {}
      }
      # Mix in a new behavior
      my clay mixinmap rest $class
      my reply set Content-Type application/json
      # Pass off control to a method brought in by the mixin
      tailcall my RestContent
    }

# Clay in Practcl

Practcl is a Tcl based make system. It can generate Tclkits, binary libaries, pure-tcl modules, and large collections of pure-tcl packages.

# Clay in Practcl

When implementing a build system, it helps to be able to refer to a generic build product, and then get more elaborate as we know more about it. Especially if we need to detect information from the compiler or environment during the course of compilation.

    set affinepath [file dirname [file normalize [info script]]]

    namespace eval ::odielib {}
    my define set initfunc OdieAffine_Init
    my add [file join $affinepath const.tcl]

    foreach file [lsort -dictionary [glob [file join $affinepath *.tcl]]] {
      if {[file tail $file] in {const.tcl cmatrixforms.tcl}} continue
      my add $file
    }
    # Run cmatrixforms at the end
    my add [file join $affinepath cmatrixforms.tcl]
    my add [file join $affinepath hardcoded_h_file.h]
    my add [file join $affinepath hardcoded_c_file.c]

# Clay in Fun Stuff

When implementing a Role Playing Game, many configuration options require a modification of behavior. Those options are orthoganal, and often interact with one another.

# Clay in Fun Stuff

Player | Yes |
Species | Human |
Gender | Male |
Class | Wizard |

# Clay in Fun Stuff

Player | Yes |  | ::gilgamesh::core/avatar
Species | Human |  | ::gilgamesh::species/human
Gender | Male |  | ::gilgamesh::gender/male
Class | Wizard |  | ::gilgamesh::class/wizard

# Clay in Fun Stuff

    # Create a database record
    GAME object create {
      uuid PLAYER
      name Hypnotoad
      core avatar
      species human
      gender male
      class  wizard
    }
    # Implement the object from that database record
    set obj [GAME object wake PLAYER]

# Developing in Clay

Clay injects a method into oo::class and oo::object called clay.

The clay method manages all of the interactions with the framework.

ALL OF THEM

# Structured Data

The principle function of the clay method is to implement a dictionary tree that is stored internally in the object.

And in the class

And all of the mixins

And all of the ancestors of the class and the mixins

# Structured Data

    ::oo::class create foo {}
    foo clay set this/means something
    set obj [foo new]
    $obj clay get this/means
    > something

    # Can use file system style paths, or dict style paths
    $obj clay get this means
    > something

# Structured Data

    ::oo::class create foo {}
    foo clay set this means something
    ::oo::class create bar {
      superclass foo
    }
    bar clay set this is madness

    set obj [bar new]
    $obj clay get this means
    > something
    $obj clay get this is
    > madness
    $obj clay get this
    > means something is madness

# Object can overwrite class data

    $obj clay set this is Sanity
    $obj clay get this is
    > Sanity

# And ... a bug

    $obj clay get this
    > means something is madness

    ###
    # SHOULD HAVE BEEN
    ###
    > means something is Sanity

# This stuff isn't easy

Managing mixins and a stuctured data is EASY.

Doing it properly... well.... my tests are getting longer than the implementation.

# Delegation

    sqlite3 ::DB ~/db/mydatabase.sqlite

    oo::class create ::mydb {
      method dump_record {table rowid} {
        # <db> goes out to the database
        my <db> eval "select * from $table where rowid=:rowid" record {}
        return [array get record]
      }
    }
    $obj clay delegate db ::DB
    $obj clay mixin ::mydb
    set data [$obj dump_record object PLAYER]

# Mixin Events

    # Script right before a class is removed
    ::mydb clay set mixin unmap-script {puts "Db...gone"}
    $obj clay mixin

    # Script as a class is mixed in
    ::mydb clay set mixin map-script {puts "Db...present"}
    # Script as another class is mixed in
    ::foo clay set mixin react-script {puts "I saw what you did there..."}

    $obj clay mixin ::mydb
    > Db...Present
    > I saw what you did there...

# Introspection

    $obj clay provenance this/means
    > ::foo
    $obj clay provenance this/is
    > ::bar
    $obj ancestors
    ::bar ::foo ::oo::class

# Option Passing

    $obj clay set this/is Sanity
    $obj clay get this/is
    > Sanity
    $obj clay provenance this/is
    > self

# Option Passing

    $obj clay set config {
      color blue
      flavor blueberry
    }
    $obj clay get config color
    > blue

# Clay Dialect

Populating clay can get a bit... verbose. To make clay feel more like a part of oo, Clay includes an oo::dialect.

    clay::define myclass {
      clay set is keyword yes

      # Variable you want initialized
      Variable running 0

      # Dict for which the initial value is a merge of all
      # of the classes in the object
      Dict state {
        running 0
        completed 0
      }
    }

# Clay Dialect

Method Ensembles

    clay::define myclass {
      Ensemble state::get args {
        my variable state
        tailcall dict get $state {*}$args
      }
      Ensemble state::set args {
        my variable state
        dict set state {*}$args
      }
    }

# Clay Dialect

Method Ensembles

    $obj mixin ::myclass
    $obj state get running
    0
    $obj state get notexists
    > error "Unknown method notexists, valid: get set"

# Clay Dialect

Method Ensembles follow interitance

    clay::define ::myotherclass {
      superclass ::myclass
      Ensemble state::notexists args {
        return "It's here"
      }
    }
    $obj state set running 1
    $obj mixin ::myotherclass
    $obj state get running
    > 1
    $obj state get notexists
    > It's here

# ...End of Prepared Slides

Live demos or hasty retreat.