views as arrays

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
}