Updated 2007-11-19 16:54:22 by LV

CMCc Some code to map metakit views to arrays of dictionaries
    namespace eval dattach {
	variable debug 0
	proc Write {view selector array var el op} {
	    upvar $var val

	    variable debug
	    if {$debug} {
		puts stderr "Write: '$view' '$selector' '$array' '$var' '$el' '$op'"
		puts stderr "V: $val($el)"
		catch {[dict get $val($el)]} err
		puts stderr "Err: $err"
	    }

	    set row [::mk::select $view $selector $el]

	    if {[llength $row]} {
		eval ::mk::set ${view}.$row [dict get $val($el)] $selector $el
	    } else {
		eval ::mk::row append $view [dict get $val($el)] $selector $el
	    }
	}

	proc Read {view selector array var el op} {
	    upvar $var val

	    variable debug
	    if {$debug} {
		puts stderr "Read: '$view' '$selector' '$array' '$var' '$el' '$op'"
	    }

	    set row [::mk::select $view $selector $el]
	    if {[llength $row]} {
		set val($el) [eval dict create [::mk::get ${view}.$row]]
	    } else {
		error "dattach can't read \"${array}\(${el}\): no such element in array"
	    }
	}

	proc Unset {view selector array var el op} {
	    upvar $var val

	    variable debug
	    if {$debug} {
		puts stderr "Unset: '$view' '$selector' '$array' '$var' '$el' '$op'"
	    }

	    if {$el == ""} {
		puts stderr "Trace: unset1 $view"
		::mk::loop el1 ${view} {
		    #catch {puts stderr "Trace: unset2 $el1 [::mk::get $el1]"}
		    catch {::mk::row delete $el1}
		}
		puts stderr "Trace: unset done"
	    } else {
		set row [::mk::select $view $selector $el]
		::mk::row delete ${view}.$row
	    }
	}

	proc attach {view array {selector ""}} {
	    if {$selector == ""} {
		set selector [lindex [::mk::view  layout $view] 0]
	    }

	    variable debug
	    if {$debug} {
		puts stderr "dattach:  $array to $view $selector"
	    }

	    upvar $array a
	    set a() ""
	    unset a()
	    trace add variable a unset [list ::dattach::Unset $view $selector $array ]
	    trace add variable a read [list ::dattach::Read $view $selector $array ]
	    trace add variable a write [list ::dattach::Write $view $selector $array ]
	}

	proc snort {view array {selector ""}} {
	    uplevel ::dattach::attach $view $array $selector
	    upvar $array a
	    ::mk::loop el1 ${view} {
		set sel [eval dict create [::mk::get $el1]]
		set a([dict get $sel $selector]) $sel
	    }
	}

	namespace export attach snort
    }

and here are a few tests:

if {$argv0 == info script} {
    package require Mk4tcl

    #set ::dattach::debug 1

    proc dumpit {heading {var shoesize}} {
	puts "*** $heading"
	upvar $var array
	foreach last [array names array] {
	    if {[catch {puts "$last: $array($last)"} error]} {
		puts "error on $var element $last: $error"
	    }
	}
	puts "---"
    }

    set db [mk::file open db /tmp/datafile.mk]
    set vw [mk::view layout db.people {last first shoesize:I}]

    # fill in some db rows
    mk::row append $vw last "Lennon" first "John" shoesize 44
    mk::row append $vw last "Gordon" first "Flash" shoesize 42
    mk::row append $vw last "Hendrix" first "Jimi" shoesize 49
    mk::file commit db

    ::dattach::attach $vw shoesize last

    dumpit "attach gets rows lazily - so this will be empty"

    set x $shoesize(Lennon)
    set x $shoesize(Gordon)
    dumpit "Fetch some attached values"

    set shoesize(Cass) [dict create first Mama shoesize 40 alive n]
    dumpit "Create a new Row" shoesize

    dict set shoesize(Cass) last Lennon ;# note, this will have no effect
    dumpit "changing the key field has no effect"

    ::dattach::snort $vw shoes last
    dumpit "snort gets all rows in existence at creation time" shoes

    dict set shoes(Lennon) shoesize -1
    dumpit "changing a different attached variable has an effect on all" shoes

    # note - we take the first match on key
    ::dattach::snort $vw size shoesize
    dumpit "By Shoesize" size

    dict set shoesize(Cass) shoesize 44
    dumpit "array names can get out of sync on snorted arrays\n*** Note: this could be fixed, if trace array was." size

}