Persistent itclThis source package contains three programs:
Persist.tclwhich persists the instance vars of an incr tcl object to a metakit
PersistRemote.tcl PersistRemoteServer.tclwhich (together) form a client/server to Persist incr tcl objects over a tcp/ip connection mediated by the comm package (via the Comm wrapper).
Background: The page Persistent incr-Tcl objects got me thinking about persistence.The code presented there requires too much user intervention, I want something which handles more of the details automatically.
I've just put up a package called itclPersist on my little projects page here: http://sourceforge.net/projects/libtclpq/itclPersist consists of a Persist class and a PersistServer class (and some test code.)The idea is that you inherit from Persist, and attach to an instance of PersistServer. After that, you forget about it - all instance variables and arrays are persisted in a metakit, are refreshed at attach time, and just basically ... persist.Next step for me is to inherit from the PersistServer to make a RemotePersist class, which does all the same things, but does them on a different machine - I'll probably use comm. Next step after that is to use [Mux] multiplexer to distribute updates to a set of client/listeners.
Here's a function which constructs a script which, when evaluated in global scope, will do what I think I want.
# serialize extracts all variables and their current values into a script # such that `eval [serialize obj]' will set obj to those values proc serialize {obj {new ""}} { set result "" if {$new == ""} { set new $obj } foreach var [$obj info variable] { foreach {type name} [$obj info variable $var -type -name] break if {([namespace tail $name] != "this") && ($type != "common")} { set fromname "@itcl $obj $name" set toname "@itcl $new $name" if {[catch { append result "eval set [list \[list ${toname}\]] [list [list [set $fromname]]]\n" } err]} { puts stderr "err: $err" } } } return $result }Here's a small test:
class test_persist { common com "com val" ;# common vars are unchanged by serialize private variable priv "priv default" public variable pub "pub default" protected variable prot "prot default" public variable single single_value_should_not_be_braced # make all variables' values uppercase method change {} { foreach var {priv pub prot} { set $var [string toupper [set $var]] } } constructor {} { # change values from the default set priv {priv value $ [] # \n \{\}} set pub {pub value $ [] # \n \{\}} set prot {prot value $ [] # \n \{\}} } } class test_p1 { inherit test_persist public variable sub_pub sub_pub private variable sub_priv sub_priv method test {} { foreach var [$this info variable] { foreach {type name val} [$this info variable $var -type -name -value] break puts "$name = $val" } } constructor {} { # needs to be here } } test_p1 t puts "Vars: [t info variable]" set lower [serialize t] puts "DUMP: '$lower'" t change ;# uppercase t puts "Will be uppercase:" t test eval $lower puts "Should be lowercase:" t test ;# check change test_p1 t2 t2 change puts "Will be uppercase:" t2 test set tolower [serialize t \$T] puts "DUMP2: '$tolower'" set T t2 eval $tolower puts "Should be lowercase:" t2 testCMcC
Persistence and DistributionThis approach raises some interesting possibilities for persistence:
- Each class instance can be represented by a metakit row
- Tequila could be adapted to attach instance variables (or whole objects) in addition to simple arrays (Necessary modification to achieve this goal would be moderate.)
- checkpointing
- Config code on public variables is not executed when the serialized code is evaluated. Command class documentation says code fragment is executed whenever a public variable is modified by the built-in "configure" method - and this is literally true.
Category Itcl